Fix bugs that occur when switching to mg/dL units.
This commit is contained in:
parent
9ed89e7f8f
commit
7087b5299a
@ -9,7 +9,7 @@ __license__ = 'MIT'
|
||||
# TODO: comments -- unicode
|
||||
# TODO: prettify
|
||||
# TODO: weekly graph with each day's figures as a different-coloured line
|
||||
# TODO: verify either set of units (mmol,mg/dl) works with the data
|
||||
# TODO: verify either set of units (mmol/L,mg/dl) works with the data
|
||||
|
||||
import argparse
|
||||
import csv
|
||||
@ -39,17 +39,20 @@ VALID_UNITS = [UNIT_MGDL, UNIT_MMOLL]
|
||||
# When averaging, set the period to this number of minutes
|
||||
INTERVAL = 15
|
||||
# Maximum gluclose value to display (TODO: mmol/mg)
|
||||
GRAPH_MAX = 21
|
||||
GRAPH_MAX = 21
|
||||
GRAPH_MIN = 1
|
||||
DEFAULT_HIGH = 8
|
||||
DEFAULT_LOW = 4
|
||||
|
||||
# Set colour for below-target maxmins
|
||||
# Colour for below-target maxmins
|
||||
RED = '#d71920'
|
||||
# Set colour for above-target maxmins
|
||||
# Colour for above-target maxmins
|
||||
YELLOW = '#f1b80e'
|
||||
# Set colour for graph lines
|
||||
# Colour for graph lines
|
||||
BLUE = '#02538f'
|
||||
# Set colour for median glucose box
|
||||
# Colour for median glucose box
|
||||
GREEN = '#009e73'
|
||||
# Set colour for median A1c box
|
||||
# Colour for median A1c box
|
||||
BOXYELLOW = '#e69f00'
|
||||
|
||||
def main():
|
||||
@ -64,10 +67,22 @@ def main():
|
||||
''' This could be done directly from glucometerutils instead of via CSV '''
|
||||
with open(args.input_file, 'r', newline='') as f:
|
||||
rows = from_csv(f)
|
||||
|
||||
|
||||
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 args.units == UNIT_MMOLL and (args.high == DEFAULT_HIGH or args.low == DEFAULT_LOW):
|
||||
mean = round(np.mean([l['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)
|
||||
|
||||
|
||||
''' 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 '''
|
||||
rows = fill_gaps(rows, interval=dt.timedelta(minutes=10))
|
||||
@ -100,7 +115,7 @@ def main():
|
||||
data = {}
|
||||
for row in rows:
|
||||
mpdate = dt.datetime.combine(rows[0]['date'], row.get('date').time())
|
||||
data[mdates.date2num(mpdate)] = {
|
||||
data[mdates.date2num(mpdate)] = {
|
||||
'value' : row.get('value'),
|
||||
'comment' : row.get('comment'),
|
||||
}
|
||||
@ -110,7 +125,7 @@ def main():
|
||||
intervaldata = {}
|
||||
for i in intervals:
|
||||
mpdate = dt.datetime.combine(rows[0]['date'], i)
|
||||
intervaldata[mdates.date2num(mpdate)] = {
|
||||
intervaldata[mdates.date2num(mpdate)] = {
|
||||
'max' : intervals.get(i).get('max'),
|
||||
'min' : intervals.get(i).get('min'),
|
||||
}
|
||||
@ -129,10 +144,10 @@ def main():
|
||||
ax.axhspan(args.low, args.high, facecolor='#0072b2', edgecolor='#a8a8a8', alpha=0.2, zorder=5)
|
||||
|
||||
''' The maxmined curve of maximum and minimum values '''
|
||||
generate_plot(intervaldata,
|
||||
generate_plot(intervaldata,
|
||||
ax=ax,
|
||||
transforms={'spline':False, 'maxmin':True},
|
||||
args=args,
|
||||
args=args,
|
||||
color='#979797',
|
||||
alpha=0.5,
|
||||
)
|
||||
@ -140,8 +155,8 @@ def main():
|
||||
generate_plot(data,
|
||||
ax=ax,
|
||||
transforms={'bezier':True, 'avga1c':a_median, \
|
||||
'color':[RED, BLUE, RED], 'boundaries':[0, args.low, args.high, GRAPH_MAX]},
|
||||
args=args,
|
||||
'color':[RED, BLUE, RED], 'boundaries':[args.graph_min, args.low, args.high, args.graph_max]},
|
||||
args=args,
|
||||
color=BLUE,
|
||||
)
|
||||
|
||||
@ -166,11 +181,11 @@ def main():
|
||||
day = monday + dt.timedelta(days=dow)
|
||||
if row.get('date').date() == day.date():
|
||||
weekrows.append(row)
|
||||
|
||||
|
||||
data = {}
|
||||
for row in weekrows:
|
||||
mpdate = dt.datetime.combine(monday, row.get('date').time())
|
||||
data[mdates.date2num(mpdate)] = {
|
||||
data[mdates.date2num(mpdate)] = {
|
||||
'value' : row.get('value'),
|
||||
'comment' : row.get('comment'),
|
||||
}
|
||||
@ -179,7 +194,7 @@ def main():
|
||||
intervaldata = {}
|
||||
for i in intervals:
|
||||
mpdate = dt.datetime.combine(monday.date(), i)
|
||||
intervaldata[mdates.date2num(mpdate)] = {
|
||||
intervaldata[mdates.date2num(mpdate)] = {
|
||||
'max' : intervals.get(i).get('max'),
|
||||
'min' : intervals.get(i).get('min'),
|
||||
}
|
||||
@ -200,19 +215,19 @@ def main():
|
||||
ax.axhspan(args.low, args.high, facecolor='#0072b2', edgecolor='#a8a8a8', alpha=0.2, zorder=5)
|
||||
|
||||
''' The maxmined curve of maximum and minimum values '''
|
||||
generate_plot(intervaldata,
|
||||
generate_plot(intervaldata,
|
||||
ax=ax,
|
||||
transforms={'spline':False, 'maxmin':True, 'avga1c':a_median},
|
||||
args=args,
|
||||
args=args,
|
||||
color='#979797',
|
||||
alpha=0.5,
|
||||
)
|
||||
|
||||
generate_plot(data,
|
||||
generate_plot(data,
|
||||
ax=ax,
|
||||
transforms={'bezier':True, \
|
||||
'color':[RED, BLUE, RED], 'boundaries':[0, args.low, args.high, GRAPH_MAX]},
|
||||
args=args,
|
||||
'color':[RED, BLUE, RED], 'boundaries':[args.graph_min, args.low, args.high, args.graph_max]},
|
||||
args=args,
|
||||
color=BLUE,
|
||||
)
|
||||
|
||||
@ -231,7 +246,7 @@ def main():
|
||||
for row in rows:
|
||||
if row.get('date').date() == day.date():
|
||||
mpdate = dt.datetime.combine(day.date(), row.get('date').time())
|
||||
data[mdates.date2num(mpdate)] = {
|
||||
data[mdates.date2num(mpdate)] = {
|
||||
'value' : row.get('value'),
|
||||
'comment' : row.get('comment'),
|
||||
}
|
||||
@ -251,18 +266,18 @@ def main():
|
||||
''' Draw the target range '''
|
||||
ax.axhspan(args.low, args.high, facecolor='#0072b2', edgecolor='#a8a8a8', alpha=0.2, zorder=5)
|
||||
|
||||
generate_plot(data,
|
||||
generate_plot(data,
|
||||
ax=ax,
|
||||
transforms={'spline':True, 'label':True, 'avgglucose':g_median, 'avga1c':a_median},
|
||||
args=args,
|
||||
args=args,
|
||||
color=BLUE,
|
||||
|
||||
|
||||
)
|
||||
''' For max higher than target high '''
|
||||
generate_plot(data,
|
||||
generate_plot(data,
|
||||
ax=ax,
|
||||
transforms={'spline':True, 'fill':True},
|
||||
args=args,
|
||||
args=args,
|
||||
)
|
||||
|
||||
''' Save the graph to the output PDF if we're at the end of the page '''
|
||||
@ -308,13 +323,17 @@ def generate_plot(data, ax=None, transforms={}, args=[], **plot_args):
|
||||
x_min = mdates.date2num(firstminute)
|
||||
x_max = mdates.date2num(lastminute)
|
||||
ax.set_xlim(x_min, x_max)
|
||||
ax.set_ylim(0, GRAPH_MAX)
|
||||
ax.set_ylim(args.graph_min, args.graph_max)
|
||||
''' Calculate the time intervals in 2 hour segments '''
|
||||
xtimes = []
|
||||
time = firstminute
|
||||
while time < lastminute:
|
||||
xtimes.append(time)
|
||||
time += dt.timedelta(hours=2)
|
||||
if args.units == UNIT_MMOLL:
|
||||
y_tick_freq = 2
|
||||
else:
|
||||
y_tick_freq = convert_glucose_unit(2, UNIT_MMOLL)
|
||||
|
||||
''' Formatting for axis labels, using date calculations from above '''
|
||||
ax.set_xlabel('Time', fontsize=9)
|
||||
@ -322,16 +341,16 @@ def generate_plot(data, ax=None, transforms={}, args=[], **plot_args):
|
||||
ax.grid(axis='x', color = '#f0f0f0', zorder=1)
|
||||
ax.set_xticks(xtimes)
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M"))
|
||||
ax.xaxis.set_ticks_position('none')
|
||||
ax.xaxis.set_ticks_position('none')
|
||||
for tick in ax.xaxis.get_major_ticks():
|
||||
tick.label1.set_horizontalalignment('left')
|
||||
|
||||
ax.set_ylabel('Blood Glucose (' + args.units + ')', fontsize=9)
|
||||
ax.set_ybound(0, GRAPH_MAX)
|
||||
ax.set_ybound(args.graph_min, args.graph_max)
|
||||
ax.grid(axis='y', color = '#d0d0d0', linestyle = (1,(0.5,2)), zorder=1)
|
||||
ax.set_yticks([a for a in range(0, GRAPH_MAX, 2)])
|
||||
ax.set_yticks([a for a in range(int(args.graph_min), int(args.graph_max), int(y_tick_freq))])
|
||||
ax.yaxis.set_major_formatter(mticker.FormatStrFormatter("%d"))
|
||||
ax.yaxis.set_ticks_position('none')
|
||||
ax.yaxis.set_ticks_position('none')
|
||||
|
||||
|
||||
if 'maxmin' in transforms and transforms['maxmin'] is True:
|
||||
@ -347,19 +366,17 @@ def generate_plot(data, ax=None, transforms={}, args=[], **plot_args):
|
||||
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
|
||||
y = f(x)
|
||||
|
||||
|
||||
elif transform == 'spline' and transforms[transform] is True:
|
||||
''' Use SciPy's UnivariateSpline for transforming (s is transforming factor) '''
|
||||
if args.units == UNIT_MMOLL:
|
||||
s = 8
|
||||
else:
|
||||
s = convert_glucose_unit(12, UNIT_MMOLL)
|
||||
if not maxmin:
|
||||
curve = interpolate.UnivariateSpline(x=x, y=y, k=3, s=8)
|
||||
curve = interpolate.UnivariateSpline(x=x, y=y, k=3, s=s)
|
||||
y = curve(x)
|
||||
#else:
|
||||
# TODO Apply spline to each curve?
|
||||
#curve1 = interpolate.UnivariateSpline(x=x, y=y, k=3, s=5)
|
||||
#curve2 = interpolate.UnivariateSpline(x=x, y=z, k=3, s=5)
|
||||
#y = curve1(x)
|
||||
#z = curve2(x)
|
||||
|
||||
|
||||
elif transform == 'bezier' and transforms[transform] is True:
|
||||
''' Create bezier function for transforming (s is transforming factor) '''
|
||||
def bezier(points, s=100):
|
||||
@ -369,13 +386,11 @@ def generate_plot(data, ax=None, transforms={}, args=[], **plot_args):
|
||||
for t in np.linspace(0, 1, s):
|
||||
u = np.power(t, r) * np.power(1 - t, n - r - 1) * b
|
||||
yield t, u @ points
|
||||
|
||||
|
||||
''' The binomial calculation for the bezier curve overflows with arrays of 1020 or more elements,
|
||||
For large arrays, get a smaller slice of the full array.
|
||||
For large arrays, get a smaller slice of the full array.
|
||||
Do this by removing every nth element from the array '''
|
||||
n = 5
|
||||
while len(p) > 1000:
|
||||
p = np.delete(p, np.arange(0, len(p), n), axis=0)
|
||||
while len(x) > 1000:
|
||||
x = np.delete(x, np.arange(0, len(x), n), axis=0)
|
||||
y = np.delete(y, np.arange(0, len(y), n), axis=0)
|
||||
@ -383,18 +398,15 @@ def generate_plot(data, ax=None, transforms={}, args=[], **plot_args):
|
||||
if not maxmin:
|
||||
curve = np.array([c for _, c in bezier(np.array([x,y]).T, 250)])
|
||||
(x, y) = (curve[:,0], curve[:,1])
|
||||
#else:
|
||||
# TODO Apply bezier to each curve? Will this work for x?
|
||||
#while len(q) > 1000:
|
||||
# q = np.delete(q, np.arange(0, len(q), n), axis=0)
|
||||
#curve1 = np.array([c for _, c in bezier(p[:], 250)])
|
||||
#curve2 = np.array([c for _, c in bezier(q[:], 250)])
|
||||
#(x, y) = (curve1[:,0], curve1[:,1])
|
||||
#(x, z) = (curve2[:,0], curve2[:,1])
|
||||
|
||||
|
||||
''' Add the mean or median glucose and A1c values '''
|
||||
if transform == 'avgglucose' and isinstance(transforms[transform], (int, float)):
|
||||
ax.annotate('Median glucose: %.1f%s' % (round(transforms['avgglucose'], 1), args.units), fontsize=9, \
|
||||
if args.units == UNIT_MMOLL:
|
||||
gmtext = 'Median glucose: %.1f%s' % (round(transforms['avgglucose'], 1), args.units)
|
||||
else:
|
||||
gmtext = 'Median glucose: %.0f%s' % (round(transforms['avgglucose'], 1), args.units)
|
||||
|
||||
ax.annotate(gmtext, fontsize=9, \
|
||||
xy=(0.95, 0.85), xycoords='axes fraction', verticalalignment='top', horizontalalignment='right', \
|
||||
zorder=40, bbox=dict(facecolor=GREEN, edgecolor='#009e73', alpha=0.7, pad=8), \
|
||||
)
|
||||
@ -404,8 +416,8 @@ 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
|
||||
# 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 transform == 'label' and transforms[transform] is True:
|
||||
for a, b, label in zip(x, y, z):
|
||||
@ -413,7 +425,7 @@ def generate_plot(data, ax=None, transforms={}, args=[], **plot_args):
|
||||
#print(label)
|
||||
ax.annotate(
|
||||
label,
|
||||
xy=(a, GRAPH_MAX-6),
|
||||
xy=(a, args.graph_max-6),
|
||||
rotation=45,
|
||||
zorder=25,
|
||||
)
|
||||
@ -449,16 +461,16 @@ def generate_plot(data, ax=None, transforms={}, args=[], **plot_args):
|
||||
return ax
|
||||
|
||||
def parse_entry(data, icons, fmt='%Y-%m-%d %H:%M:%S'):
|
||||
''' Parse a row to create the icons and modify the timestamp
|
||||
|
||||
''' Parse a row to create the icons and modify the timestamp
|
||||
|
||||
Args:
|
||||
data: a dict containing the entries 'timestamp' and 'comment'
|
||||
data: a dict containing the entries 'timestamp' and 'comment'
|
||||
icons: bool indicating whether to display food/injection icons on the graph
|
||||
date_format: the format of the timestamp in data
|
||||
|
||||
|
||||
Returns:
|
||||
data: the modified dict
|
||||
|
||||
|
||||
Raises:
|
||||
ValueError if an incorrectly-formatted date exists in data['timestamp']
|
||||
'''
|
||||
@ -479,8 +491,8 @@ 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
|
||||
# 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)
|
||||
@ -511,14 +523,17 @@ def parse_entry(data, icons, fmt='%Y-%m-%d %H:%M:%S'):
|
||||
''' Convert value from string to float '''
|
||||
data['value'] = float(data.get('value'))
|
||||
|
||||
# XXX convert everything to mg/dL for testing
|
||||
#data['value'] = float(round(data.get('value') * 18.0, 0))
|
||||
|
||||
return data
|
||||
|
||||
def list_days_and_weeks(data, trim_weeks=192):
|
||||
''' Create a dictionary of the days and weeks that occur in the CSV
|
||||
|
||||
''' Create a dictionary of the days and weeks that occur in the CSV
|
||||
|
||||
Args:
|
||||
data: a dict containing a 'timestamp' entry
|
||||
trim_weeks: the minimum number of entries a week should have in order to be considered for
|
||||
trim_weeks: the minimum number of entries a week should have in order to be considered for
|
||||
a weekly average graph. A reading taken every 15 minutes over two days would yield 192 readings.
|
||||
|
||||
Returns:
|
||||
@ -592,7 +607,7 @@ def calculate_max_min(data):
|
||||
datas: a dict with elements 'timestamp' and 'value'
|
||||
|
||||
Returns:
|
||||
intervals: a dictionary of minimum and maximum values for a a time period
|
||||
intervals: a dictionary of minimum and maximum values for a a time period
|
||||
|
||||
Raises:
|
||||
ValueError if an incorrectly-formatted date exists in data['timestamp']
|
||||
@ -602,7 +617,7 @@ def calculate_max_min(data):
|
||||
date = d.get('date')
|
||||
date = date.replace(minute=int(date.minute/INTERVAL)*INTERVAL, second=0, microsecond=0, tzinfo=None)
|
||||
time = date.time()
|
||||
|
||||
|
||||
if not time in intervals:
|
||||
intervals[time] = {}
|
||||
intervals[time]['min'] = d.get('value')
|
||||
@ -618,11 +633,11 @@ def calculate_max_min(data):
|
||||
|
||||
def fill_gaps(rows, interval, maxinterval=dt.timedelta(days=1)):
|
||||
''' Fill in time gaps that may exist in a set of rows, in order to smooth drawn curves and fills
|
||||
|
||||
|
||||
Args:
|
||||
rows: a dict containing a 'date' entry (the result of parse_entry())
|
||||
interval: a datetime.timedelta object that defines the maximum distance allowed between two entries
|
||||
maxinterval: a datetime.timedelta object that defines the maximum amount of time, over which we ignore
|
||||
maxinterval: a datetime.timedelta object that defines the maximum amount of time, over which we ignore
|
||||
the difference between two consecutive entries
|
||||
|
||||
Returns:
|
||||
@ -661,7 +676,7 @@ def fill_gaps(rows, interval, maxinterval=dt.timedelta(days=1)):
|
||||
item = {
|
||||
'date': period,
|
||||
'meal': '',
|
||||
'value': float('%.2f' % val),
|
||||
'value': float('%.2f' % val),
|
||||
'comment': '',
|
||||
'timestamp': period.strftime('%Y-%m-%dT%H:%M:%S'),
|
||||
'measure_method': 'Estimate',
|
||||
@ -689,7 +704,7 @@ def verify_units(units = None, high = None, low = None):
|
||||
elif re.search('mg', units, flags=re.IGNORECASE) is not None:
|
||||
units = UNIT_MMOLL
|
||||
elif isinstance(high, (int, float)) or isinstance(low, (int, float)):
|
||||
''' If units are not specified by the arguments or calling function, let's assume they are
|
||||
''' If units are not specified by the arguments or calling function, let's assume they are
|
||||
mg/dL if the high is more than 35 or the low more than 20 '''
|
||||
if (isinstance(high, (int, float)) and (high > 35) or
|
||||
isinstance(low, (int, float)) and (low > 20)):
|
||||
@ -726,16 +741,22 @@ def parse_arguments():
|
||||
default='mmol/L', choices=(UNIT_MGDL, UNIT_MMOLL),
|
||||
help=('The measurement units used (mmol/L or mg/dL).'))
|
||||
parser.add_argument(
|
||||
'--low', action='store', required=False, type=float, default=4,
|
||||
'--low', action='store', required=False, type=float, default=DEFAULT_LOW,
|
||||
help=('Minimum of target glucose range.'))
|
||||
parser.add_argument(
|
||||
'--high', action='store', required=False, type=float, default=8,
|
||||
'--high', action='store', required=False, type=float, default=DEFAULT_HIGH,
|
||||
help=('Maximum of target glucose range.'))
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
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
|
||||
else:
|
||||
args.graph_max = convert_glucose_unit(GRAPH_MAX, UNIT_MMOLL)
|
||||
args.graph_min = convert_glucose_unit(GRAPH_MIN, 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:
|
||||
@ -745,7 +766,7 @@ def parse_arguments():
|
||||
|
||||
def from_csv(csv_file, newline=''):
|
||||
'''Returns the reading as a formatted comma-separated value string.'''
|
||||
data = csv.reader(csv_file, delimiter=',', quotechar='"')
|
||||
data = csv.reader(csv_file, delimiter=',', quotechar='"')
|
||||
fields = [ 'timestamp', 'value', 'meal', 'measure_method', 'comment' ]
|
||||
rows = []
|
||||
for row in data:
|
||||
@ -753,6 +774,34 @@ def from_csv(csv_file, newline=''):
|
||||
rows.append(item)
|
||||
return rows
|
||||
|
||||
def convert_glucose_unit(value, from_unit, to_unit=None):
|
||||
"""Convert the given value of glucose level between units.
|
||||
|
||||
Args:
|
||||
value: The value of glucose in the current unit
|
||||
from_unit: The unit value is currently expressed in
|
||||
to_unit: The unit to conver the value to: the other if empty.
|
||||
|
||||
Returns:
|
||||
The converted representation of the blood glucose level.
|
||||
|
||||
Raises:
|
||||
exceptions.InvalidGlucoseUnit: If the parameters are incorrect.
|
||||
"""
|
||||
if from_unit not in VALID_UNITS:
|
||||
raise exceptions.InvalidGlucoseUnit(from_unit)
|
||||
|
||||
if from_unit == to_unit:
|
||||
return value
|
||||
|
||||
if to_unit is not None:
|
||||
if to_unit not in VALID_UNITS:
|
||||
raise exceptions.InvalidGlucoseUnit(to_unit)
|
||||
|
||||
if from_unit is UNIT_MGDL:
|
||||
return round(value / 18.0, 2)
|
||||
else:
|
||||
return round(value * 18.0, 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
Loading…
Reference in New Issue
Block a user