formatting fixes

This commit is contained in:
James Woglom
2024-09-29 22:10:41 -04:00
parent 210bcddbd3
commit 81d1e80b59
16 changed files with 117 additions and 93 deletions
+15 -11
View File
@@ -9,6 +9,8 @@ from .api import TConnectApi
from .process import process_time_range
from .autoupdate import Autoupdate
from .sync.tandemsource.autoupdate import TandemSourceAutoupdate
from .sync.tandemsource.choose_device import ChooseDevice as TandemSourceChooseDevice
from .sync.tandemsource.process import ProcessTimeRange as TandemSourceProcessTimeRange
from .check import check_login
from .nightscout import NightscoutApi
from .features import DEFAULT_FEATURES, ALL_FEATURES
@@ -94,22 +96,24 @@ def main(*args, **kwargs):
nightscout = NightscoutApi(NS_URL, NS_SECRET, skip_verify=NS_SKIP_TLS_VERIFY, ignore_conn_errors=NS_IGNORE_CONN_ERRORS)
if args.check_login:
return check_login(tconnect, time_start, time_end)
# NOT YET MIGRATED
# if args.check_login:
# return check_login(tconnect, time_start, time_end)
logging.warning("THIS VERSION OF TCONNECTSYNC READS DATA FROM TANDEM SOURCE, AND MAY CONTAIN BUGS!")
logging.info("You may notice different behavior compared to older versions which utilized t:connect data sources.")
logging.info("To report a bug or to get help, see https://github.com/jwoglom/tconnectsync/issues")
logging.info("Enabled features: " + ", ".join(args.features))
if args.tandem_source:
if args.auto_update:
u = TandemSourceAutoupdate(secret)
sys.exit(u.process(tconnect, nightscout, time_start, time_end, args.pretend, features=args.features))
if args.check_login:
args.pretend = True
if args.auto_update:
print("Starting auto-update between", time_start, "and", time_end, "(PRETEND)" if args.pretend else "")
u = Autoupdate(secret)
u = TandemSourceAutoupdate(secret)
sys.exit(u.process(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, features=args.features)
print("Added", added, "items")
tconnectDevice = TandemSourceChooseDevice(secret, tconnect).choose()
added = TandemSourceProcessTimeRange(tconnect, nightscout, tconnectDevice, pretend=args.pretend, features=args.features).process(time_start, time_end)
sys.exit(added)
+9 -9
View File
@@ -29,7 +29,7 @@ class Autoupdate:
def process(self, tconnect, nightscout, time_start, time_end, pretend, features=None):
if features is None:
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.
@@ -55,7 +55,7 @@ class Autoupdate:
# just already be in sync with tconnect's pump data.
if self.last_event_index:
# Find the timestamp of the last time we've successfully obtained data,
# Find the timestamp of the last time we've successfully obtained data,
# or the time when the autoupdate run started, if we haven't at all.
last_action_or_start = self.last_successful_process_time_range
if not last_action_or_start:
@@ -69,7 +69,7 @@ class Autoupdate:
if (now - last_action_or_start) >= 60 * self.secret.AUTOUPDATE_FAILURE_MINUTES:
logger.error(AutoupdateFailureError(
("%s: An event index change was recorded, but no new data was found via the API. " % datetime.datetime.now()) +
"The %s was %d minutes ago. This is a problem with tconnectsync." %
"The %s was %d minutes ago. This is a problem with tconnectsync." %
("last processed event" if self.last_successful_process_time_range else "start of autoupdate", (now - last_action_or_start)//60)))
if self.secret.AUTOUPDATE_RESTART_ON_FAILURE:
@@ -77,9 +77,9 @@ class Autoupdate:
return 1
else:
logger.warning(AutoupdateFailureWarning(("%s: An event index change was recorded, but no new data was found via the API. " % datetime.datetime.now()) +
"The %s was %d minutes ago. Resetting TConnectApi to attempt to solve this problem." %
"The %s was %d minutes ago. Resetting TConnectApi to attempt to solve this problem." %
("last processed event" if self.last_successful_process_time_range else "start of autoupdate", (now - last_action_or_start)//60)))
# As a stop-gap, try to re-initialize TConnectApi (triggering a re-login)
# Use __class__ instead of direct TConnectApi invocation to avoid initializing a real TConnectApi over a fake
tconnect = tconnect.__class__(self.secret.TCONNECT_EMAIL, self.secret.TCONNECT_PASSWORD)
@@ -140,12 +140,12 @@ class Autoupdate:
# If it's been 3 loops since the last time we found new data,
# then we're not in sync with the rate at which pump data is being
# uploaded, so
# uploaded, so
if len(self.time_diffs_between_attempts) >= 3:
# The pump hasn't sent us data that, based on previous cadence, we were expecting
logger.warning(AutoupdateNoIndexChangeWarning("Sleeping %d seconds after unexpected no index change based on previous cadence. (New data might be delayed.)" %
logger.warning(AutoupdateNoIndexChangeWarning("Sleeping %d seconds after unexpected no index change based on previous cadence. (New data might be delayed.)" %
int(self.secret.AUTOUPDATE_UNEXPECTED_NO_INDEX_SLEEP_SECONDS)))
logger.debug("Last event time: %s, time diffs between attempts: %s" % (self.last_event_time, self.time_diffs_between_attempts))
time.sleep(self.secret.AUTOUPDATE_UNEXPECTED_NO_INDEX_SLEEP_SECONDS)
@@ -167,7 +167,7 @@ class Autoupdate:
if len(self.time_diffs_between_updates) > 10:
self.time_diffs_between_updates = self.time_diffs_between_updates[1:]
# If we have less than 3 data points,
# If we have less than 3 data points,
if len(self.time_diffs_between_updates) > 2:
sleep_secs = sum(self.time_diffs_between_updates) / len(self.time_diffs_between_updates)
+6 -6
View File
@@ -82,13 +82,13 @@ class NightscoutApi:
try:
ret = internal(False)
except ApiException as e:
logger.warning("last_uploaded_entry with no t_to_space: %s", e)
#logger.warning("last_uploaded_entry with no t_to_space: %s", e)
ret = None
if ret is None and (time_start or time_end):
try:
ret = internal(True)
except ApiException as e:
logger.warning("last_uploaded_entry with t_to_space: %s", e)
#logger.warning("last_uploaded_entry with t_to_space: %s", e)
ret = None
if ret is not None:
logger.warning("last_uploaded_entry with eventType=%s time_start=%s time_end=%s only returned data when timestamps contained a space" % (eventType, time_start, time_end))
@@ -98,7 +98,7 @@ class NightscoutApi:
logger.warn('Ignoring ConnectionError because ignore_conn_errors=true', e)
else:
raise e
def last_uploaded_bg_entry(self, time_start=None, time_end=None):
def internal(t_to_space):
dateFilter = time_range('dateString', time_start, time_end, t_to_space=t_to_space)
@@ -112,7 +112,7 @@ class NightscoutApi:
if j and len(j) > 0:
return j[0]
return None
try:
ret = internal(False)
if ret is None and (time_start or time_end):
@@ -140,7 +140,7 @@ class NightscoutApi:
if j and len(j) > 0:
return j[0]
return None
try:
ret = internal(False)
if ret is None and (time_start or time_end):
@@ -164,7 +164,7 @@ class NightscoutApi:
if status.status_code != 200:
raise Exception('HTTP error status code (%d) from Nightscout: %s' % (status.status_code, status.text))
return status.json()
"""
Returns information on the currently configured Nightscout profile data store
(contains all profiles in Nightscout under one mongo object).
+2 -29
View File
@@ -6,6 +6,7 @@ import arrow
from ...features import DEFAULT_FEATURES
from .process import ProcessTimeRange
from .choose_device import ChooseDevice
logger = logging.getLogger(__name__)
@@ -37,33 +38,8 @@ class TandemSourceAutoupdate:
while True:
logger.debug("autoupdate loop")
now = time.time()
pumpEventMetadata = tconnect.tandemsource.pump_event_metadata()
serialNumberToPump = {p['serialNumber']: p for p in pumpEventMetadata}
logger.info(f'Found {len(serialNumberToPump)} pumps: {serialNumberToPump.keys()}')
tconnectDevice = None
if self.secret.PUMP_SERIAL_NUMBER and str(self.secret.PUMP_SERIAL_NUMBER) != '11111111':
if not str(self.secret.PUMP_SERIAL_NUMBER) in serialNumberToPump.keys():
raise InvalidSerialNumber(f'Serial number {self.secret.PUMP_SERIAL_NUMBER} is not present on your account: choose one of {", ".join(serialNumberToPump.keys())}')
tconnectDevice = serialNumberToPump[str(self.secret.PUMP_SERIAL_NUMBER)]
logger.info(f'Using pump with serial: {tconnectDevice["serialNumber"]} (tconnectDeviceId: {tconnectDevice["tconnectDeviceId"]}, last seen: {tconnectDevice["maxDateWithEvents"]})')
else:
maxDateSeen = None
for pump in pumpEventMetadata:
if not tconnectDevice:
tconnectDevice = pump
maxDateSeen = arrow.get(pump['maxDateWithEvents'])
else:
if arrow.get(pump['maxDateWithEvents']) > maxDateSeen:
maxDateSeen = arrow.get(pump['maxDateWithEvents'])
tconnectDevice = pump
logger.info(f'Using most recent pump (serial: {tconnectDevice["serialNumber"]}, tconnectDeviceId: {tconnectDevice["tconnectDeviceId"]}, last seen: {tconnectDevice["maxDateWithEvents"]})')
tconnectDevice = ChooseDevice(self.secret, tconnect).choose()
cur_max_date_with_events = arrow.get(tconnectDevice['maxDateWithEvents'])
if not self.last_max_date_with_events or cur_max_date_with_events > self.cur_max_date_with_events:
@@ -191,7 +167,4 @@ class AutoupdateNoNewDataDetectedError(AutoupdateError):
pass
class AutoupdateNoIndexChangeWarning(AutoupdateWarning):
pass
class InvalidSerialNumber(AutoupdateError):
pass
@@ -0,0 +1,48 @@
import arrow
import logging
logger = logging.getLogger(__name__)
class ChooseDevice:
def __init__(self, secret, tconnect):
self.secret = secret
self.tconnect = tconnect
def choose(self):
tconnect = self.tconnect
pumpEventMetadata = tconnect.tandemsource.pump_event_metadata()
serialNumberToPump = {p['serialNumber']: p for p in pumpEventMetadata}
logger.info(f'Found {len(serialNumberToPump)} pumps: {serialNumberToPump.keys()}')
tconnectDevice = None
if self.secret.PUMP_SERIAL_NUMBER and str(self.secret.PUMP_SERIAL_NUMBER) != '11111111':
if not str(self.secret.PUMP_SERIAL_NUMBER) in serialNumberToPump.keys():
raise InvalidSerialNumber(f'Serial number {self.secret.PUMP_SERIAL_NUMBER} is not present on your account: choose one of {", ".join(serialNumberToPump.keys())}')
tconnectDevice = serialNumberToPump[str(self.secret.PUMP_SERIAL_NUMBER)]
logger.info(f'Using pump with serial: {tconnectDevice["serialNumber"]} (tconnectDeviceId: {tconnectDevice["tconnectDeviceId"]}, last seen: {tconnectDevice["maxDateWithEvents"]})')
else:
maxDateSeen = None
for pump in pumpEventMetadata:
if not tconnectDevice:
tconnectDevice = pump
maxDateSeen = arrow.get(pump['maxDateWithEvents'])
else:
if arrow.get(pump['maxDateWithEvents']) > maxDateSeen:
maxDateSeen = arrow.get(pump['maxDateWithEvents'])
tconnectDevice = pump
logger.info(f'Using most recent pump (serial: {tconnectDevice["serialNumber"]}, tconnectDeviceId: {tconnectDevice["tconnectDeviceId"]}, last seen: {tconnectDevice["maxDateWithEvents"]})')
return tconnectDevice
class InvalidSerialNumber(RuntimeError):
def __str__(self):
return "%s: %s" % (self.__class__.__name__, super().__str__())
@@ -61,7 +61,6 @@ class ProcessTimeRange:
clazz = EventClass.for_event(event)
if clazz:
processed_count += 1
for_eventclass[clazz.name].append(event)
count_by_eventclass = {k: len(v) for k,v in for_eventclass.items()}
@@ -61,13 +61,13 @@ class ProcessAlarm:
def alarm_to_nsentry(self, event):
if type(event) == eventtypes.LidAlarmActivated:
return NightscoutEntry.alarm(
created_at = event.eventTimestamp,
created_at = event.eventTimestamp.format(),
reason = event.alarmid,
pump_event_id = event.eventId
pump_event_id = "%s" % event.eventId
)
elif type(event) == eventtypes.LidMalfunctionActivated:
return NightscoutEntry.alarm(
created_at = event.eventTimestamp,
created_at = event.eventTimestamp.format(),
reason = "Malfunction",
pump_event_id = event.eventId
pump_event_id = "%s" % event.eventId
)
@@ -71,15 +71,15 @@ class ProcessBasal:
return NightscoutEntry.basal(
value = event.commandedbasalrate,
duration_mins = duration.seconds / 60,
created_at = start,
created_at = start.format(),
reason = ', '.join(bitmask_to_list(event.changetype)),
pump_event_id = event.eventId
pump_event_id = "%s" % event.eventId
)
if type(event) == eventtypes.LidBasalDelivery:
return NightscoutEntry.basal(
value = event.commandedRate,
duration_mins = duration.seconds / 60,
created_at = start,
created_at = start.format(),
reason = ', '.join(bitmask_to_list(event.commandedRateSource)),
pump_event_id = event.eventId
pump_event_id = "%s" % event.eventId
)
@@ -61,6 +61,6 @@ class ProcessBasalResume:
def resume_to_nsentry(self, event):
if type(event) == eventtypes.LidPumpingResumed:
return NightscoutEntry.basalresume(
created_at = event.eventTimestamp,
pump_event_id = event.eventId
created_at = event.eventTimestamp.format(),
pump_event_id = "%s" % event.eventId
)
@@ -61,7 +61,7 @@ class ProcessBasalSuspension:
def suspension_to_nsentry(self, event):
if type(event) == eventtypes.LidPumpingSuspended:
return NightscoutEntry.basalsuspension(
created_at = event.eventTimestamp,
created_at = event.eventTimestamp.format(),
reason = ', '.join(bitmask_to_list(event.suspendreason)),
pump_event_id = event.eventId
pump_event_id = "%s" % event.eventId
)
@@ -99,7 +99,7 @@ class ProcessBolus:
return NightscoutEntry.bolus(
bolus = bolusCompleted.insulindelivered,
carbs = bolusRequested1.carbamount if bolusRequested1 and bolusRequested1.carbamount>0 else None,
created_at = bolusCompleted.eventTimestamp,
created_at = bolusCompleted.eventTimestamp.format(),
notes = (eventtypes.LidBolusRequestedMsg2.OptionsMap[bolusRequested2.optionsRaw] if bolusRequested2 else '') + suffix,
bg = bolusRequested1.BG if bolusRequested1 and bolusRequested1.BG > 0 else None,
pump_event_id = ",".join(event_ids)
@@ -80,21 +80,21 @@ class ProcessCartridge:
def cart_to_nsentry(self, cartFilled):
return NightscoutEntry.sitechange(
created_at = cartFilled.eventTimestamp,
created_at = cartFilled.eventTimestamp.format(),
reason = "Cartridge Filled" + (" (%du filled)" % round(cartFilled.v2Volume) if cartFilled.v2Volume else ""),
pump_event_id = cartFilled.eventId
pump_event_id = "%s" % cartFilled.eventId
)
def cannula_to_nsentry(self, cannulaFilled):
return NightscoutEntry.sitechange(
created_at = cannulaFilled.eventTimestamp,
created_at = cannulaFilled.eventTimestamp.format(),
reason = "Cannula Filled" + (" (%du primed)" % round(cannulaFilled.primesize) if cannulaFilled.primesize else ""),
pump_event_id = cannulaFilled.eventId
pump_event_id = "%s" % cannulaFilled.eventId
)
def tubing_to_nsentry(self, tubingFilled):
return NightscoutEntry.sitechange(
created_at = tubingFilled.eventTimestamp,
created_at = tubingFilled.eventTimestamp.format(),
reason = "Tubing Filled" + (" (%du primed)" % round(tubingFilled.primesize) if tubingFilled.primesize else ""),
pump_event_id = tubingFilled.eventId
pump_event_id = "%s" % tubingFilled.eventId
)
@@ -65,19 +65,19 @@ class ProcessCGMAlert:
def alert_to_nsentry(self, alert):
if type(alert) == eventtypes.LidCgmAlertActivated:
return NightscoutEntry.cgm_alert(
created_at = alert.eventTimestamp,
created_at = alert.eventTimestamp.format(),
reason = ("CGM Alert (%s)" % alert.dalertid) if alert.dalertid else "CGM Alert (Unknown)",
pump_event_id = alert.eventId
pump_event_id = "%s" % alert.eventId
)
elif type(alert) == eventtypes.LidCgmAlertActivatedDex:
return NightscoutEntry.cgm_alert(
created_at = alert.eventTimestamp,
created_at = alert.eventTimestamp.format(),
reason = ("Dexcom CGM Alert (%s)" % alert.dalertid) if alert.dalertid else "Dexcom CGM Alert (Unknown)",
pump_event_id = alert.eventId
pump_event_id = "%s" % alert.eventId
)
elif type(alert) == eventtypes.LidCgmAlertActivatedFsl2:
return NightscoutEntry.cgm_alert(
created_at = alert.eventTimestamp,
created_at = alert.eventTimestamp.format(),
reason = ("Libre CGM Alert (%s)" % alert.dalertid) if alert.dalertid else "Libre CGM Alert (Unknown)",
pump_event_id = alert.eventId
pump_event_id = "%s" % alert.eventId
)
@@ -69,6 +69,6 @@ class ProcessCGMReading:
def to_nsentry(self, event):
return NightscoutEntry.entry(
sgv = event.currentglucosedisplayvalue,
created_at = self.timestamp_for(event),
pump_event_id = event.eventId,
created_at = self.timestamp_for(event).format(),
pump_event_id = "%s" % event.eventId,
)
@@ -65,19 +65,19 @@ class ProcessCGMStartJoinStop:
def to_nsentry(self, event):
if type(event) in EventClass._CGM_START:
return NightscoutEntry.cgm_start(
created_at = event.eventTimestamp,
created_at = event.eventTimestamp.format(),
reason = "CGM Session Started",
pump_event_id = event.eventId
pump_event_id = "%s" % event.eventId
)
elif type(event) in EventClass._CGM_JOIN:
return NightscoutEntry.cgm_join(
created_at = event.eventTimestamp,
created_at = event.eventTimestamp.format(),
reason = "CGM Session Joined",
pump_event_id = event.eventId
pump_event_id = "%s" % event.eventId
)
elif type(event) in EventClass._CGM_STOP:
return NightscoutEntry.cgm_stop(
created_at = event.eventTimestamp,
created_at = event.eventTimestamp.format(),
reason = "CGM Session Stopped",
pump_event_id = event.eventId
pump_event_id = "%s" % event.eventId
)
@@ -153,7 +153,7 @@ class ProcessUserMode:
duration_mins = (stop.eventTimestamp - start.eventTimestamp).seconds / 60
return NightscoutEntry.activity(
created_at=start.eventTimestamp,
created_at=start.eventTimestamp.format(),
reason=reason,
duration=duration_mins,
event_type=SLEEP_EVENTTYPE,
@@ -168,7 +168,7 @@ class ProcessUserMode:
duration_mins = (time_end - start.eventTimestamp).seconds / 60
return NightscoutEntry.activity(
created_at=start.eventTimestamp,
created_at=start.eventTimestamp.format(),
reason=reason + " - " + NOT_ENDED if reason else NOT_ENDED,
duration=duration_mins,
event_type=SLEEP_EVENTTYPE,
@@ -187,7 +187,7 @@ class ProcessUserMode:
duration_mins = (stop.eventTimestamp - start.eventTimestamp).seconds / 60
return NightscoutEntry.activity(
created_at=start.eventTimestamp,
created_at=start.eventTimestamp.format(),
reason=reason,
duration=duration_mins,
event_type=EXERCISE_EVENTTYPE,
@@ -200,7 +200,7 @@ class ProcessUserMode:
duration_mins = (time_end.eventTimestamp - start.eventTimestamp).seconds / 60
return NightscoutEntry.activity(
created_at=start.eventTimestamp,
created_at=start.eventTimestamp.format(),
reason=reason + " - " + NOT_ENDED,
duration=duration_mins,
event_type=EXERCISE_EVENTTYPE,