Final cleanup.
This commit is contained in:
parent
b070cc51ce
commit
4d77fac610
@ -1,6 +1,9 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
''' Utility to convert data from a glucometer into charts. '''
|
||||
__author__ = 'Timothy Allen'
|
||||
__email__ = 'tim@treehouse.org.za'
|
||||
__license__ = 'MIT'
|
||||
|
||||
''' Included are the Noto Sans and IcoGluco font sets.
|
||||
Noto Sans is licensed under the SIL Open Font License version 1.1
|
||||
@ -12,11 +15,6 @@
|
||||
licensed under Creative Commons BY 3.0, <http://creativecommons.org/licenses/by/3.0/>
|
||||
'''
|
||||
|
||||
__author__ = 'Timothy Allen'
|
||||
__email__ = 'tim@treehouse.org.za'
|
||||
__license__ = 'MIT'
|
||||
|
||||
# TODO: comments -- unicode/images/np.array
|
||||
# TODO: weekly graph with each day's figures as a different-coloured line
|
||||
|
||||
import argparse
|
||||
@ -40,30 +38,32 @@ import re
|
||||
from scipy import interpolate
|
||||
from scipy.special import binom
|
||||
import sys
|
||||
import pprint
|
||||
|
||||
# Constants for units
|
||||
''' Constants for units '''
|
||||
UNIT_MGDL = 'mg/dL'
|
||||
UNIT_MMOLL = 'mmol/L'
|
||||
VALID_UNITS = [UNIT_MGDL, UNIT_MMOLL]
|
||||
|
||||
# When averaging, set the period to this number of minutes
|
||||
''' When averaging, set the period to this number of minutes '''
|
||||
INTERVAL = 15
|
||||
# Maximum gluclose value to display (TODO: mmol/mg)
|
||||
GRAPH_MAX = 21
|
||||
GRAPH_MIN = 1
|
||||
''' Set the default high and low in mmol/L; it will be reset to mg/dL if neccessary '''
|
||||
DEFAULT_HIGH = 8
|
||||
DEFAULT_LOW = 4
|
||||
''' Maximum glucose value to display '''
|
||||
GRAPH_MAX_MMOLL = 21
|
||||
GRAPH_MIN_MMOLL = 0
|
||||
GRAPH_MAX_MGDL = 400
|
||||
GRAPH_MIN_MGDL = 0
|
||||
|
||||
# Colour for below-target maxmins
|
||||
''' Colour for below-target maxmins '''
|
||||
RED = '#d71920'
|
||||
# Colour for above-target maxmins
|
||||
'''' Colour for above-target maxmins '''
|
||||
YELLOW = '#f1b80e'
|
||||
# Colour for graph lines
|
||||
''' Colour for graph lines '''
|
||||
BLUE = '#02538f'
|
||||
# Colour for median glucose box
|
||||
''' Colour for median glucose box '''
|
||||
GREEN = '#009e73'
|
||||
# Colour for median A1c box
|
||||
''' Colour for median A1c box '''
|
||||
BOXYELLOW = '#e69f00'
|
||||
|
||||
def main():
|
||||
@ -71,8 +71,6 @@ def main():
|
||||
raise Exception(
|
||||
'Unsupported Python version, please use at least Python 3.2')
|
||||
|
||||
pp = pprint.PrettyPrinter(depth=6)
|
||||
|
||||
args = parse_arguments()
|
||||
|
||||
''' This could be done directly from glucometerutils instead of via CSV '''
|
||||
@ -82,17 +80,17 @@ def main():
|
||||
for row in rows:
|
||||
row = parse_entry(row, args.icons)
|
||||
|
||||
# If we're on the default values for units, highs and lows, check that the average
|
||||
# value is under 35 (assuming that average mmol/L < 35 and average mg/dL > 35)
|
||||
''' If we're on the default values for units, highs and lows, check that the average
|
||||
value is under 35 (assuming that average mmol/L < 35 and average mg/dL > 35) '''
|
||||
if args.units == UNIT_MMOLL and (args.high == DEFAULT_HIGH or args.low == DEFAULT_LOW):
|
||||
mean = round(np.mean([l.get('value') for l in rows]), 1)
|
||||
if mean > 35:
|
||||
args.units = UNIT_MGDL
|
||||
args.high = convert_glucose_unit(args.high, UNIT_MMOLL)
|
||||
args.low = convert_glucose_unit(args.low, UNIT_MMOLL)
|
||||
args.graph_max = convert_glucose_unit(args.graph_max, UNIT_MMOLL)
|
||||
args.graph_min = convert_glucose_unit(args.graph_min, UNIT_MMOLL)
|
||||
|
||||
''' Manually specify max and min for mg/dL '''
|
||||
args.graph_max = GRAPH_MAX_MGDL
|
||||
args.graph_min = GRAPH_MIN_MGDL
|
||||
|
||||
''' Fill in gaps that might exist in the data, in order to smooth the curves and fills '''
|
||||
''' We're using 8 minute gaps in order to have more accurate fills '''
|
||||
@ -111,9 +109,13 @@ def main():
|
||||
rcParams['font.sans-serif'] = ['Calibri','Verdana','Geneva','Arial','Helvetica','DejaVu Sans','Bitstream Vera Sans','sans-serif']
|
||||
rcParams['mathtext.default'] = 'regular'
|
||||
|
||||
# Load custom fonts for the icon sets
|
||||
''' Load custom fonts for the icon sets
|
||||
At present, backend_pdf does not parse unicode correctly, and unicode
|
||||
characters from many fonts that lack proper glyph names are massed together
|
||||
and printed as the same character. The IcoGluco font, generated from Noto Sans and
|
||||
custom icons on IcoMoon, works around this. '''
|
||||
if args.icons:
|
||||
args.customfont = import_font('fonts/icogluco.ttf') # Works
|
||||
args.customfont = import_font('fonts/icogluco.ttf')
|
||||
#args.customfont = import_font('fonts/OpenSansEmoji.ttf') # Alternate working font
|
||||
|
||||
nrows = args.graphs_per_page
|
||||
@ -302,18 +304,16 @@ def main():
|
||||
|
||||
|
||||
def generate_plot(data, ax=None, transforms={}, args=[], **plot_args):
|
||||
pp = pprint.PrettyPrinter(depth=6)
|
||||
|
||||
(x, y, z, p, q) = (list(), list(), list(), list(), list())
|
||||
for (key, value) in sorted(data.items()):
|
||||
# Time
|
||||
''' Time '''
|
||||
a = key
|
||||
if 'maxmin' in transforms:
|
||||
# If a max and a min exists, initialise them to y and z
|
||||
''' If a max and a min exists, initialise them to y and z '''
|
||||
b = value.get('max')
|
||||
c = value.get('min')
|
||||
else:
|
||||
# Glucose and comment
|
||||
''' Glucose and comment '''
|
||||
b = value.get('value')
|
||||
c = value.get('comment', '')
|
||||
x.append(a)
|
||||
@ -342,7 +342,7 @@ def generate_plot(data, ax=None, transforms={}, args=[], **plot_args):
|
||||
if args.units == UNIT_MMOLL:
|
||||
y_tick_freq = 2
|
||||
else:
|
||||
y_tick_freq = convert_glucose_unit(2, UNIT_MMOLL)
|
||||
y_tick_freq = 50
|
||||
|
||||
''' Formatting for axis labels, using date calculations from above '''
|
||||
ax.set_xlabel('Time', fontsize=9)
|
||||
@ -373,7 +373,8 @@ def generate_plot(data, ax=None, transforms={}, args=[], **plot_args):
|
||||
''' Use SciPy's interp1d for linear transforming '''
|
||||
if not maxmin:
|
||||
f = interpolate.interp1d(x, y, kind='linear')
|
||||
x = np.linspace(x.min(), x.max(), 50) # 50 is number of points to make between x.max & x.min
|
||||
''' 50 is number of points to make between x.max & x.min '''
|
||||
x = np.linspace(x.min(), x.max(), 50)
|
||||
y = f(x)
|
||||
|
||||
elif transform == 'spline' and transforms.get(transform) is True:
|
||||
@ -425,9 +426,6 @@ def generate_plot(data, ax=None, transforms={}, args=[], **plot_args):
|
||||
zorder=40, bbox=dict(facecolor=BOXYELLOW, edgecolor='#e69f00', alpha=0.7, pad=8), \
|
||||
)
|
||||
|
||||
# XXX At present, backend_pdf does not parse unicode correctly, and all recent
|
||||
# unicode chacters that lack proper glyph names are massed together and printed
|
||||
# as the same character
|
||||
if args.units == UNIT_MMOLL:
|
||||
y_offset = 6
|
||||
else:
|
||||
@ -454,7 +452,6 @@ def generate_plot(data, ax=None, transforms={}, args=[], **plot_args):
|
||||
elif key == 'Food':
|
||||
symbol += '\N{GREEN APPLE}'
|
||||
symbol += '$'
|
||||
print(symbol)
|
||||
ax.annotate(
|
||||
symbol,
|
||||
xy=(x_pos, args.graph_max-y_offset),
|
||||
@ -495,9 +492,9 @@ def generate_plot(data, ax=None, transforms={}, args=[], **plot_args):
|
||||
return ax
|
||||
|
||||
def import_font(fontname):
|
||||
''' Turns a relative font path into a matplotlib font property. '''
|
||||
basedir = os.path.dirname(os.path.abspath(__file__))
|
||||
fontdir = os.path.join(basedir, 'fonts')
|
||||
fontpath = os.path.join(fontdir, fontname)
|
||||
fontpath = os.path.join(basedir, fontname)
|
||||
if not os.path.exists(fontpath):
|
||||
raise UserError("Font %s does not exist" % fontpath)
|
||||
prop = fm.FontProperties(fname=fontpath)
|
||||
@ -528,7 +525,6 @@ def parse_entry(data, icons, fmt='%Y-%m-%d %H:%M:%S'):
|
||||
ctype = relevant.group(1)
|
||||
cvalue = relevant.group(2)
|
||||
|
||||
#cvalue = re.sub('(\d+)(\.\d+)?', '\g<1>', cvalue)
|
||||
''' Convert floating point-style strings (2.0) to integer-style strings (2) '''
|
||||
try:
|
||||
cvalue = int(float(cvalue))
|
||||
@ -541,13 +537,6 @@ def parse_entry(data, icons, fmt='%Y-%m-%d %H:%M:%S'):
|
||||
if re.search('Long', ctype) is not None:
|
||||
cvalue += 'L'
|
||||
|
||||
# XXX At present, backend_pdf does not parse unicode correctly, and all recent
|
||||
# unicode chacters that lack proper glyph names are massed together and printed
|
||||
# as the same character
|
||||
# XXX Alternatives include replacing the glyph with an image, or a Path
|
||||
#ctype = re.sub('Food', '$\mathcal{\N{GREEN APPLE}}$', ctype, flags=re.IGNORECASE)
|
||||
#ctype = re.sub('Rapid-acting insulin', '$\mathcal{\N{SYRINGE}}^\mathrm{'+cvalue+'}$', ctype, flags=re.IGNORECASE)
|
||||
#ctype = re.sub('Long-acting insulin', '$\mathcal{\N{SYRINGE}}^\mathrm{'+cvalue+'}$', ctype, flags=re.IGNORECASE)
|
||||
ctype = re.sub('Rapid-acting insulin', 'Insulin', ctype, flags=re.IGNORECASE)
|
||||
ctype = re.sub('Long-acting insulin', 'Insulin', ctype, flags=re.IGNORECASE)
|
||||
|
||||
@ -800,11 +789,15 @@ def parse_arguments():
|
||||
args.pagesize = verify_pagesize(args.pagesize)
|
||||
args.units = verify_units(args.units, args.high, args.low)
|
||||
if args.units == UNIT_MMOLL:
|
||||
args.graph_max = GRAPH_MAX
|
||||
args.graph_min = GRAPH_MIN
|
||||
args.graph_max = GRAPH_MAX_MMOLL
|
||||
args.graph_min = GRAPH_MIN_MMOLL
|
||||
else:
|
||||
args.graph_max = convert_glucose_unit(GRAPH_MAX, UNIT_MMOLL)
|
||||
args.graph_min = convert_glucose_unit(GRAPH_MIN, UNIT_MMOLL)
|
||||
args.graph_max = GRAPH_MAX_MGDL
|
||||
args.graph_min = GRAPH_MIN_MGDL
|
||||
''' If the user specified the units but not the high or low targets, set them now '''
|
||||
if args.high == DEFAULT_HIGH or args.low == DEFAULT_LOW:
|
||||
args.high = convert_glucose_unit(args.high, UNIT_MMOLL)
|
||||
args.low = convert_glucose_unit(args.low, UNIT_MMOLL)
|
||||
|
||||
''' Ensure we have a valid number of graphs_per_page '''
|
||||
if not isinstance(args.graphs_per_page, int) or args.graphs_per_page < 1:
|
||||
|
Loading…
Reference in New Issue
Block a user