summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Cady <d@jerkface.net>2017-01-26 11:53:35 +0000
committerAndrew Cady <d@jerkface.net>2017-01-26 13:04:13 +0000
commit5b79e4eb51a6140c3747ee135af36b4c2c285484 (patch)
treefccc294b3bb13e1d3daec7050cfe989e3be469a7
parent896265b0b523014ea4b4105e3fe87bdc234e8b62 (diff)
dexcom_dumper: improved JSON output; new option --hours
-rw-r--r--dexcom_reader/dexcom_dumper.py108
1 files changed, 76 insertions, 32 deletions
diff --git a/dexcom_reader/dexcom_dumper.py b/dexcom_reader/dexcom_dumper.py
index e279433..c531dfc 100644
--- a/dexcom_reader/dexcom_dumper.py
+++ b/dexcom_reader/dexcom_dumper.py
@@ -3,14 +3,16 @@ import readdata
3import sys 3import sys
4import json 4import json
5import requests 5import requests
6from pytz import UTC
6from sys import stdout, stderr 7from sys import stdout, stderr
7from datetime import timedelta, datetime 8from datetime import timedelta, datetime
8from time import sleep 9from time import sleep
10from itertools import islice, takewhile
11from database_records import GenericTimestampedRecord
9 12
10from optparse import OptionParser 13from optparse import OptionParser
11 14
12G5_IS_DEFAULT = True 15G5_IS_DEFAULT = True
13DEFAULT_PAGE_COUNT = 2
14 16
15parser = OptionParser() 17parser = OptionParser()
16parser.add_option("--g4", action="store_false", dest="g5", default=G5_IS_DEFAULT, help="use Dexcom G4 instead of Dexcom G5") 18parser.add_option("--g4", action="store_false", dest="g5", default=G5_IS_DEFAULT, help="use Dexcom G4 instead of Dexcom G5")
@@ -18,8 +20,10 @@ parser.add_option("--g5", action="store_true", dest="g5", default=G5_IS_DEFAULT
18 20
19parser.add_option("-a", "--all", action="store_const", dest="command", const="dump_everything", help="dump all available records") 21parser.add_option("-a", "--all", action="store_const", dest="command", const="dump_everything", help="dump all available records")
20parser.add_option("-p", "--poll", action="store_const", dest="command", const="poll", help="poll for latest CGM record") 22parser.add_option("-p", "--poll", action="store_const", dest="command", const="poll", help="poll for latest CGM record")
23parser.add_option("--test", action="store_const", dest="command", const="test", help="test")
21 24
22parser.add_option("-n", type="int", dest="num_records", default=DEFAULT_PAGE_COUNT, help="number of pages of CGM records to display") 25parser.add_option("--hours", type="int", dest="hours", default=None, help="display N most recent hours of CGM records")
26parser.add_option("-n", type="int", dest="num_records", default=None, help="number of CGM records to display")
23parser.add_option("-v", "--verbose", action="store_true", dest="verbose", help="verbosity (currently for debugging)") 27parser.add_option("-v", "--verbose", action="store_true", dest="verbose", help="verbosity (currently for debugging)")
24parser.add_option("-H", "--human", action="store_true", dest="human", help="print human-readable times") 28parser.add_option("-H", "--human", action="store_true", dest="human", help="print human-readable times")
25parser.add_option("-j", "--json", action="store_true", dest="json", help="print JSON output") 29parser.add_option("-j", "--json", action="store_true", dest="json", help="print JSON output")
@@ -33,6 +37,9 @@ HUMAN = options.human
33JSON = options.json 37JSON = options.json
34HOST = options.host 38HOST = options.host
35 39
40if command is 'dump_cgm' and options.num_records is None and not options.hours:
41 options.hours = 2
42
36def get_dexcom_reader(): 43def get_dexcom_reader():
37 if options.g5: 44 if options.g5:
38 dd = readdata.DexcomG5.FindDevice() 45 dd = readdata.DexcomG5.FindDevice()
@@ -43,25 +50,30 @@ def get_dexcom_reader():
43 50
44dr = get_dexcom_reader() 51dr = get_dexcom_reader()
45 52
46def dump_everything(): 53def parseable_record_types():
47
48# record_types = ['METER_DATA', 'INSERTION_TIME', 'USER_EVENT_DATA', 'CAL_SET', 'SENSOR_DATA']
49
50 unparseable = ['FIRMWARE_PARAMETER_DATA', 'RECEIVER_LOG_DATA', 'USER_SETTING_DATA', 'MAX_VALUE'] 54 unparseable = ['FIRMWARE_PARAMETER_DATA', 'RECEIVER_LOG_DATA', 'USER_SETTING_DATA', 'MAX_VALUE']
51 parsed_to_xml = ['MANUFACTURING_DATA', 'PC_SOFTWARE_PARAMETER'] 55 parsed_to_xml = ['MANUFACTURING_DATA', 'PC_SOFTWARE_PARAMETER']
52 skip = unparseable + parsed_to_xml 56 skip = unparseable + parsed_to_xml
53 record_types = filter(lambda v: not v in skip, constants.RECORD_TYPES) 57 return filter(lambda v: not v in skip, constants.RECORD_TYPES)
54 58
55 for t in record_types: 59def choose_range(rs):
60 if options.hours:
61 now = dr.ReadSystemTime()
62 when = now - timedelta(hours=options.hours)
63 return takewhile(lambda r: r.system_time > when, rs)
64 else:
65 return islice(rs, options.num_records)
66
67def dump_everything():
68 for t in parseable_record_types():
56 print t + ":" 69 print t + ":"
57 for r in dr.ReadRecords(t): 70 for r in choose_range(dr.iter_records(t)):
58 print r 71 print toJSON(r) if JSON else r
59 72
60def dump_cgm(): 73def dump_cgm():
61 cgm_records = dr.ReadRecords('EGV_DATA', options.num_records) 74 for cr in reversed(list(choose_range(dr.iter_records('EGV_DATA')))):
62 for cr in cgm_records:
63 if not cr.display_only: 75 if not cr.display_only:
64 print cr 76 print toJSON(cr) if JSON else cr
65 77
66def recent(t): 78def recent(t):
67 now = dr.ReadSystemTime() 79 now = dr.ReadSystemTime()
@@ -74,7 +86,7 @@ def print_verbose(s):
74 86
75def read_recent_egv_data(): 87def read_recent_egv_data():
76 try: 88 try:
77 r = dr.ReadRecords('EGV_DATA', options.num_records)[-1] 89 r = dr.ReadRecords('EGV_DATA', 1)[-1]
78 if recent(r) and not r.is_special and not r.display_only: 90 if recent(r) and not r.is_special and not r.display_only:
79 return r 91 return r
80 else: 92 else:
@@ -119,31 +131,25 @@ def POST(path, json_str):
119 resp = requests.post(HOST + path, data=json_str, 131 resp = requests.post(HOST + path, data=json_str,
120 headers={'Content-type': 132 headers={'Content-type':
121 'application/json'}) 133 'application/json'})
134 print_verbose(resp.text)
135 resp.raise_for_status()
122 return True 136 return True
123 except: 137 except:
138 print_verbose(sys.exc_info())
124 return False 139 return False
125 140
126def strftime_JSON(t):
127 return t.strftime('%FT%T.0Z');
128
129def send_ping(now): 141def send_ping(now):
130 if HOST: 142 if HOST:
131 POST('/ping', json.dumps(strftime_JSON(now))) 143 POST('/ping', toJSON(now))
132 144
133def print_cgm_bg(now, r): 145def print_cgm_bg(now, r):
134 if HOST or JSON: 146 if HOST:
135 json_str = json.dumps({ 147 r.trend_arrow.replace('45_', 'DIAGONAL_', 1)
136 'bgeEventTime': strftime_JSON(r.system_time), 148 POST('/bgevent', toJSON(r))
137 'bgeGlucose': r.glucose, 149 send_ping(now)
138 'bgeTrendArrow': r.trend_arrow.replace('45_', 'DIAGONAL_', 1), 150 return
139 'bgeDisplayOnly': r.display_only 151 elif JSON:
140 }) 152 print toJSON(r)
141 if HOST:
142 POST('/bgevent', json_str)
143 send_ping(now)
144 return
145 if JSON:
146 print json_str
147 else: 153 else:
148 print '%s: %s %s' % (format_times(now, r.system_time), r.glucose, r.trend_arrow) 154 print '%s: %s %s' % (format_times(now, r.system_time), r.glucose, r.trend_arrow)
149 stdout.flush() 155 stdout.flush()
@@ -168,7 +174,45 @@ def poll():
168 if (next_reading > 0): 174 if (next_reading > 0):
169 sleep_verbose(next_reading) 175 sleep_verbose(next_reading)
170 176
177def since(when, rectype='EGV_DATA'):
178 return reversed(list(takewhile(lambda r: r.system_time > when, dr.iter_records(rectype))))
179
180def test():
181 now = dr.ReadSystemTime()
182 POST('/ping', toJSON(now))
183
184 for t in parseable_record_types():
185 rs = dr.ReadRecords(t, 1)
186 if not rs:
187 continue
188 r=rs[-1]
189 print toJSON([t,r])
190
191class JSON_Time(json.JSONEncoder):
192 def default(self, o):
193 if isinstance(o, datetime):
194 if o.tzinfo is None:
195 return o.replace(tzinfo=UTC).isoformat()
196 else:
197 return o.isoformat()
198
199 return json.JSONEncoder.default(self, o)
200
201class JSON_CGM(JSON_Time):
202 def default(self, o):
203 if isinstance(o, GenericTimestampedRecord):
204 op={}
205 for k in o.BASE_FIELDS + o.FIELDS:
206 op[k] = getattr(o, k)
207 return op
208
209 return JSON_Time.default(self, o)
210
211def toJSON(o):
212 return json.dumps(o, cls=JSON_CGM)
213
171{"dump_everything": dump_everything, 214{"dump_everything": dump_everything,
172 "dump_cgm": dump_cgm, 215 "dump_cgm": dump_cgm,
173 "poll": poll 216 "poll": poll,
217 "test": test,
174}[command]() 218}[command]()