mirror of
https://github.com/bckelley/tconnectsync.git
synced 2026-08-24 11:44:12 -05:00
support specifying specific features to synchronize (basal, bolus, iob)
This commit is contained in:
@@ -10,6 +10,7 @@ from .process import process_time_range
|
||||
from .autoupdate import process_auto_update
|
||||
from .check import check_login
|
||||
from .nightscout import NightscoutApi
|
||||
from .features import DEFAULT_FEATURES, ALL_FEATURES
|
||||
|
||||
try:
|
||||
from .secret import (
|
||||
@@ -38,6 +39,7 @@ def parse_args(*args, **kwargs):
|
||||
parser.add_argument('--days', dest='days', type=int, default=1, help='The number of days of t:connect data to read in. Cannot be used with --from-date and --until-date.')
|
||||
parser.add_argument('--auto-update', dest='auto_update', action='store_const', const=True, default=False, help='If set, continuously checks for updates from t:connect and syncs with Nightscout.')
|
||||
parser.add_argument('--check-login', dest='check_login', action='store_const', const=True, default=False, help='If set, checks that the provided t:connect credentials can be used to log in.')
|
||||
parser.add_argument('--features', dest='features', nargs='+', default=DEFAULT_FEATURES, choices=ALL_FEATURES, help='Specifies what data should be synchronized between tconnect and Nightscout.')
|
||||
|
||||
return parser.parse_args(*args, **kwargs)
|
||||
|
||||
@@ -76,11 +78,13 @@ def main(*args, **kwargs):
|
||||
if args.check_login:
|
||||
return check_login(tconnect, time_start, time_end)
|
||||
|
||||
logging.info("Enabled features: " + ", ".join(args.features))
|
||||
|
||||
if args.auto_update:
|
||||
print("Starting auto-update between", time_start, "and", time_end, "(PRETEND)" if args.pretend else "")
|
||||
process_auto_update(tconnect, nightscout, time_start, time_end, args.pretend)
|
||||
process_auto_update(tconnect, nightscout, time_start, time_end, args.pretend, features=args.features)
|
||||
else:
|
||||
print("Processing data between", time_start, "and", time_end, "(PRETEND)" if args.pretend else "")
|
||||
added = process_time_range(tconnect, nightscout, time_start, time_end, args.pretend)
|
||||
added = process_time_range(tconnect, nightscout, time_start, time_end, args.pretend, features=args.features)
|
||||
print("Added", added, "items")
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import logging
|
||||
import sys
|
||||
|
||||
from .process import process_time_range
|
||||
from .features import DEFAULT_FEATURES
|
||||
from .secret import (
|
||||
PUMP_SERIAL_NUMBER,
|
||||
AUTOUPDATE_DEFAULT_SLEEP_SECONDS,
|
||||
@@ -18,7 +19,7 @@ logger = logging.getLogger(__name__)
|
||||
Performs the auto-update functionality. Runs indefinitely in a loop
|
||||
until stopped (ctrl+c).
|
||||
"""
|
||||
def process_auto_update(tconnect, nightscout, time_start, time_end, pretend):
|
||||
def process_auto_update(tconnect, nightscout, time_start, time_end, pretend, features=DEFAULT_FEATURES):
|
||||
# Read from android api, find exact interval to cut down on API calls
|
||||
# Refresh API token. If failure, die, have wrapper script re-run.
|
||||
|
||||
@@ -35,7 +36,7 @@ def process_auto_update(tconnect, nightscout, time_start, time_end, pretend):
|
||||
if pretend:
|
||||
logger.info('Would update now if not in pretend mode')
|
||||
else:
|
||||
added = process_time_range(tconnect, nightscout, time_start, time_end, pretend)
|
||||
added = process_time_range(tconnect, nightscout, time_start, time_end, pretend, features=features)
|
||||
logger.info('Added %d items from process_time_range' % added)
|
||||
if added == 0:
|
||||
if last_event_index:
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
"""Supported synchronization features."""
|
||||
|
||||
BASAL = "BASAL"
|
||||
BOLUS = "BOLUS"
|
||||
IOB = "IOB"
|
||||
|
||||
DEFAULT_FEATURES = [
|
||||
BASAL,
|
||||
BOLUS,
|
||||
IOB
|
||||
]
|
||||
|
||||
ALL_FEATURES = [
|
||||
BASAL,
|
||||
BOLUS,
|
||||
IOB
|
||||
]
|
||||
+16
-12
@@ -19,6 +19,7 @@ from .sync.iob import (
|
||||
ns_write_iob_events
|
||||
)
|
||||
from .parser.tconnect import TConnectEntry
|
||||
from .features import BASAL, BOLUS, IOB, DEFAULT_FEATURES
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -27,7 +28,7 @@ Given a TConnectApi object and start/end range, performs a single
|
||||
cycle of synchronizing data within the time range.
|
||||
If pretend is true, then doesn't actually write data to Nightscout.
|
||||
"""
|
||||
def process_time_range(tconnect, nightscout, time_start, time_end, pretend):
|
||||
def process_time_range(tconnect, nightscout, time_start, time_end, pretend, features=DEFAULT_FEATURES):
|
||||
logger.info("Downloading t:connect ControlIQ data")
|
||||
try:
|
||||
ciqTherapyTimelineData = tconnect.controliq.therapy_timeline(time_start, time_end)
|
||||
@@ -59,20 +60,23 @@ def process_time_range(tconnect, nightscout, time_start, time_end, pretend):
|
||||
|
||||
added = 0
|
||||
|
||||
basalEvents = process_ciq_basal_events(ciqTherapyTimelineData)
|
||||
if csvBasalData:
|
||||
logger.debug("CSV basal data found: processing it")
|
||||
add_csv_basal_events(basalEvents, csvBasalData)
|
||||
else:
|
||||
logger.debug("No CSV basal data found")
|
||||
if BASAL in features:
|
||||
basalEvents = process_ciq_basal_events(ciqTherapyTimelineData)
|
||||
if csvBasalData:
|
||||
logger.debug("CSV basal data found: processing it")
|
||||
add_csv_basal_events(basalEvents, csvBasalData)
|
||||
else:
|
||||
logger.debug("No CSV basal data found")
|
||||
|
||||
added += ns_write_basal_events(nightscout, basalEvents, pretend=pretend)
|
||||
added += ns_write_basal_events(nightscout, basalEvents, pretend=pretend)
|
||||
|
||||
bolusEvents = process_bolus_events(bolusData)
|
||||
added += ns_write_bolus_events(nightscout, bolusEvents, pretend=pretend)
|
||||
if BOLUS in features:
|
||||
bolusEvents = process_bolus_events(bolusData)
|
||||
added += ns_write_bolus_events(nightscout, bolusEvents, pretend=pretend)
|
||||
|
||||
iobEvents = process_iob_events(iobData)
|
||||
added += ns_write_iob_events(nightscout, iobEvents, pretend=pretend)
|
||||
if IOB in features:
|
||||
iobEvents = process_iob_events(iobData)
|
||||
added += ns_write_iob_events(nightscout, iobEvents, pretend=pretend)
|
||||
|
||||
logger.info("Wrote %d events to Nightscout this process cycle" % added)
|
||||
return added
|
||||
@@ -6,6 +6,7 @@ import pprint
|
||||
|
||||
from tconnectsync.process import process_time_range
|
||||
from tconnectsync.parser.nightscout import IOB_ACTIVITYTYPE, NightscoutEntry
|
||||
from tconnectsync.features import BASAL, BOLUS, IOB
|
||||
|
||||
from .api.fake import TConnectApi
|
||||
from .nightscout_fake import NightscoutApi
|
||||
@@ -68,6 +69,33 @@ class TestProcessTimeRange(unittest.TestCase):
|
||||
self.assertDictEqual(nightscout.put_entries, {})
|
||||
self.assertListEqual(nightscout.deleted_entries, [])
|
||||
|
||||
"""No data in Nightscout. Nothing should be updated in Nightscout without the BASAL feature."""
|
||||
def test_basal_data_not_updated_without_feature(self):
|
||||
tconnect = TConnectApi()
|
||||
|
||||
# datetimes are unused by the API fake
|
||||
start = datetime.datetime(2021, 4, 20, 12, 0)
|
||||
end = datetime.datetime(2021, 4, 21, 12, 0)
|
||||
|
||||
def fake_therapy_timeline(time_start, time_end):
|
||||
self.assertEqual(time_start, start)
|
||||
self.assertEqual(time_end, end)
|
||||
|
||||
return TestBasalSync.get_example_ciq_basal_events()
|
||||
|
||||
tconnect.controliq.therapy_timeline = fake_therapy_timeline
|
||||
tconnect.ws2.therapy_timeline_csv = self.stub_therapy_timeline_csv
|
||||
|
||||
nightscout = NightscoutApi()
|
||||
|
||||
nightscout.last_uploaded_entry = self.stub_last_uploaded_entry
|
||||
nightscout.last_uploaded_activity = self.stub_last_uploaded_activity
|
||||
|
||||
process_time_range(tconnect, nightscout, start, end, pretend=False, features=[BOLUS, IOB])
|
||||
|
||||
self.assertEqual(len(nightscout.uploaded_entries["treatments"]), 0)
|
||||
self.assertDictEqual(nightscout.put_entries, {})
|
||||
self.assertListEqual(nightscout.deleted_entries, [])
|
||||
|
||||
"""Two basal entries in Nightscout. Two new basal entries in tconnect."""
|
||||
def test_partial_ciq_basal_data(self):
|
||||
@@ -199,6 +227,37 @@ class TestProcessTimeRange(unittest.TestCase):
|
||||
self.assertDictEqual(nightscout.put_entries, {})
|
||||
self.assertListEqual(nightscout.deleted_entries, [])
|
||||
|
||||
"""No data in Nightscout. Nothing should be updated in Nightscout without the BOLUS feature."""
|
||||
def test_bolus_data_not_updated_without_feature(self):
|
||||
tconnect = TConnectApi()
|
||||
|
||||
# datetimes are unused by the API fake
|
||||
start = datetime.datetime(2021, 4, 20, 12, 0)
|
||||
end = datetime.datetime(2021, 4, 21, 12, 0)
|
||||
|
||||
tconnect.controliq.therapy_timeline = self.stub_therapy_timeline
|
||||
|
||||
bolusData = TestBolusSync.get_example_csv_bolus_events()
|
||||
def fake_therapy_timeline_csv(time_start, time_end):
|
||||
return {
|
||||
**self.stub_therapy_timeline_csv(time_start, time_end),
|
||||
"bolusData": bolusData,
|
||||
}
|
||||
|
||||
tconnect.ws2.therapy_timeline_csv = fake_therapy_timeline_csv
|
||||
|
||||
nightscout = NightscoutApi()
|
||||
|
||||
nightscout.last_uploaded_entry = self.stub_last_uploaded_entry
|
||||
nightscout.last_uploaded_activity = self.stub_last_uploaded_activity
|
||||
|
||||
process_time_range(tconnect, nightscout, start, end, pretend=False, features=[BASAL, IOB])
|
||||
|
||||
pprint.pprint(nightscout.uploaded_entries)
|
||||
self.assertEqual(len(nightscout.uploaded_entries["treatments"]), 0)
|
||||
self.assertDictEqual(nightscout.put_entries, {})
|
||||
self.assertListEqual(nightscout.deleted_entries, [])
|
||||
|
||||
"""No data in Nightscout. Uploads new iob reading from tconnect."""
|
||||
def test_new_ciq_iob_data(self):
|
||||
tconnect = TConnectApi()
|
||||
@@ -234,6 +293,36 @@ class TestProcessTimeRange(unittest.TestCase):
|
||||
]})
|
||||
self.assertDictEqual(nightscout.put_entries, {})
|
||||
self.assertListEqual(nightscout.deleted_entries, [])
|
||||
|
||||
"""No data in Nightscout. Nothing should be updated in Nightscout without the IOB feature."""
|
||||
def test_iob_data_not_updated_without_feature(self):
|
||||
tconnect = TConnectApi()
|
||||
|
||||
# datetimes are unused by the API fake
|
||||
start = datetime.datetime(2021, 4, 20, 12, 0)
|
||||
end = datetime.datetime(2021, 4, 21, 12, 0)
|
||||
|
||||
tconnect.controliq.therapy_timeline = self.stub_therapy_timeline
|
||||
|
||||
iobData = TestIOBSync.get_example_csv_iob_events()
|
||||
def fake_therapy_timeline_csv(time_start, time_end):
|
||||
return {
|
||||
**self.stub_therapy_timeline_csv(time_start, time_end),
|
||||
"iobData": iobData,
|
||||
}
|
||||
|
||||
tconnect.ws2.therapy_timeline_csv = fake_therapy_timeline_csv
|
||||
|
||||
nightscout = NightscoutApi()
|
||||
|
||||
nightscout.last_uploaded_entry = self.stub_last_uploaded_entry
|
||||
nightscout.last_uploaded_activity = self.stub_last_uploaded_activity
|
||||
|
||||
process_time_range(tconnect, nightscout, start, end, pretend=False, features=[BASAL, BOLUS])
|
||||
|
||||
self.assertEqual(len(nightscout.uploaded_entries["activity"]), 0)
|
||||
self.assertDictEqual(nightscout.put_entries, {})
|
||||
self.assertListEqual(nightscout.deleted_entries, [])
|
||||
|
||||
"""Existing IOB in Nightscout. Uploads new iob reading and deletes old IOB."""
|
||||
def test_updates_ciq_iob_data(self):
|
||||
|
||||
Reference in New Issue
Block a user