Skip to content

Commit

Permalink
more streamlining. now use DI for handling a single glucose result ob…
Browse files Browse the repository at this point in the history
…ject which has allowed us to simplify our method calls with room for further improvement.
  • Loading branch information
Delubear committed Oct 17, 2024
1 parent 17bd243 commit 9bd6bd1
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 173 deletions.
1 change: 1 addition & 0 deletions GlucoseTray.Domain/DependencyExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public static IServiceCollection RegisterDomainServices(this IServiceCollection
.AddScoped<INightscoutService, NightscoutService>()
.AddScoped<IDexcomService, DexcomService>()
.AddScoped<UrlAssembler, UrlAssembler>()
.AddScoped<GlucoseResult, GlucoseResult>()
.AddScoped<IGlucoseFetchService, GlucoseFetchService>();

return services;
Expand Down
32 changes: 15 additions & 17 deletions GlucoseTray.Domain/DisplayResults/AlertService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,68 +3,66 @@

namespace GlucoseTray.Domain.DisplayResults;

public class AlertService(ISettingsProxy options, IIconService iconService, IDialogService dialogService)
public class AlertService(ISettingsProxy options, IIconService iconService, IDialogService dialogService, GlucoseResult glucoseResult)
{
private readonly ISettingsProxy _options = options;
private readonly IIconService _uiService = iconService;
private readonly IDialogService _dialogService = dialogService;
private AlertLevel _currentAlertLevel = AlertLevel.None;

public void AlertNotification(GlucoseResult currentGlucoseResult)
public void AlertNotification()
{
if (currentGlucoseResult.IsStale(_options.StaleResultsThreshold))
if (glucoseResult.IsStale(options.StaleResultsThreshold))
return;

var highAlertTriggered = _options.HighAlert && IsAlertTriggered(currentGlucoseResult.MgValue, currentGlucoseResult.MmolValue, _options.HighBg, UpDown.Down);
var warningHighAlertTriggered = _options.WarningHighAlert && IsAlertTriggered(currentGlucoseResult.MgValue, currentGlucoseResult.MmolValue, _options.WarningHighBg, UpDown.Down);
var warningLowAlertTriggered = _options.WarningLowAlert && IsAlertTriggered(currentGlucoseResult.MgValue, currentGlucoseResult.MmolValue, _options.WarningLowBg, UpDown.Up);
var lowAlertTriggered = _options.LowAlert && IsAlertTriggered(currentGlucoseResult.MgValue, currentGlucoseResult.MmolValue, _options.LowBg, UpDown.Up);
var criticalLowAlertTriggered = _options.CriticalLowAlert && IsAlertTriggered(currentGlucoseResult.MgValue, currentGlucoseResult.MmolValue, _options.CriticalLowBg, UpDown.Up);
var highAlertTriggered = options.HighAlert && IsAlertTriggered(glucoseResult.MgValue, glucoseResult.MmolValue, options.HighBg, UpDown.Down);
var warningHighAlertTriggered = options.WarningHighAlert && IsAlertTriggered(glucoseResult.MgValue, glucoseResult.MmolValue, options.WarningHighBg, UpDown.Down);
var warningLowAlertTriggered = options.WarningLowAlert && IsAlertTriggered(glucoseResult.MgValue, glucoseResult.MmolValue, options.WarningLowBg, UpDown.Up);
var lowAlertTriggered = options.LowAlert && IsAlertTriggered(glucoseResult.MgValue, glucoseResult.MmolValue, options.LowBg, UpDown.Up);
var criticalLowAlertTriggered = options.CriticalLowAlert && IsAlertTriggered(glucoseResult.MgValue, glucoseResult.MmolValue, options.CriticalLowBg, UpDown.Up);

// Order matters. We need to show the most severe alert while avoid multiple alerts. (High > warning high. Critical > low > warning low)
if (highAlertTriggered)
{
if (_currentAlertLevel != AlertLevel.High)
_uiService.ShowTrayNotification("High Glucose Alert");
iconService.ShowTrayNotification("High Glucose Alert");
_currentAlertLevel = AlertLevel.High;
return;
}
if (warningHighAlertTriggered)
{
if (_currentAlertLevel != AlertLevel.High && _currentAlertLevel != AlertLevel.WarningHigh)
_uiService.ShowTrayNotification("Warning High Glucose Alert");
iconService.ShowTrayNotification("Warning High Glucose Alert");
_currentAlertLevel = AlertLevel.WarningHigh;
return;
}
if (criticalLowAlertTriggered)
{
if (_currentAlertLevel != AlertLevel.CriticalLow)
_dialogService.ShowCriticalAlert("Critical Low Glucose Alert", "Critical Low Glucose Alert");
dialogService.ShowCriticalAlert("Critical Low Glucose Alert", "Critical Low Glucose Alert");
_currentAlertLevel = AlertLevel.CriticalLow;
return;
}
if (lowAlertTriggered)
{
if (_currentAlertLevel != AlertLevel.CriticalLow && _currentAlertLevel != AlertLevel.Low)
_uiService.ShowTrayNotification("Low Glucose Alert");
iconService.ShowTrayNotification("Low Glucose Alert");
_currentAlertLevel = AlertLevel.Low;
return;
}
if (warningLowAlertTriggered)
{
if (_currentAlertLevel != AlertLevel.CriticalLow && _currentAlertLevel != AlertLevel.Low && _currentAlertLevel != AlertLevel.WarningLow)
_uiService.ShowTrayNotification("Warning Low Glucose Alert");
iconService.ShowTrayNotification("Warning Low Glucose Alert");
_currentAlertLevel = AlertLevel.WarningLow;
return;
}
_currentAlertLevel = AlertLevel.None;
}

// TODO: We shouldn't be needing both MG and MMOL here. We should be able to use one or the other.
private bool IsAlertTriggered(double glucoseValueMG, double glucoseValueMMOL, double alertThreshold, UpDown directionGlucoseShouldBeToNotAlert)
{
if (glucoseValueMMOL == 0) // If a null / default result is returned, do not trigger alerts.
return false;
if (_options.GlucoseUnit == GlucoseUnitType.MG)
if (options.GlucoseUnit == GlucoseUnitType.MG)
{
if (directionGlucoseShouldBeToNotAlert == UpDown.Down)
return glucoseValueMG >= alertThreshold;
Expand Down
2 changes: 1 addition & 1 deletion GlucoseTray.Domain/DisplayResults/IIconService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ public interface IIconService
{
void InitializeTrayIcon(EventHandler exitEvent);
void DisposeTrayIcon();
void CreateIcon(GlucoseResult glucoseResult);
void CreateIcon();
void ShowTrayNotification(string alertName);
}
}
70 changes: 28 additions & 42 deletions GlucoseTray.Domain/FetchResults/Dexcom/DexcomService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,67 +7,53 @@ namespace GlucoseTray.Domain.FetchResults.Dexcom;

public interface IDexcomService
{
Task<GlucoseResult> GetLatestReadingAsync();
Task GetLatestReadingAsync();
}

public class DexcomService(ISettingsProxy settings, ILogger<DexcomService> logger, UrlAssembler urlBuilder, IExternalCommunicationAdapter externalAdapter, DebugService debug) : IDexcomService
public class DexcomService(ISettingsProxy settings, ILogger<DexcomService> logger, UrlAssembler urlBuilder, IExternalCommunicationAdapter externalAdapter, DebugService debug, GlucoseResult glucoseResult) : IDexcomService
{
private readonly ISettingsProxy _settings = settings;
private readonly ILogger _logger = logger;
private readonly UrlAssembler _urlBuilder = urlBuilder;
private readonly IExternalCommunicationAdapter _externalAdapter = externalAdapter;
private readonly DebugService _debug = debug;

public async Task<GlucoseResult> GetLatestReadingAsync()
public async Task GetLatestReadingAsync()
{
_debug.ClearDebugText();
_debug.AddDebugText("Starting DexCom Fetch");
_debug.AddDebugText("Server: " + _urlBuilder.GetDexComServer());

GlucoseResult glucoseResult;
debug.ClearDebugText();
debug.AddDebugText("Starting DexCom Fetch");
debug.AddDebugText("Server: " + urlBuilder.GetDexComServer());

try
{
string accountId = await GetAccountId();
string sessionId = await GetSessionId(accountId);
string response = await GetApiResponse(sessionId);

_debug.AddDebugText("Attempting to deserialize");
debug.AddDebugText("Attempting to deserialize");
var data = JsonSerializer.Deserialize<List<DexcomResult>>(response)!.First();
_debug.AddDebugText("Deserialized");
debug.AddDebugText("Deserialized");

glucoseResult = MapToResult(data);
MapToResult(data);
}
catch (Exception ex)
{
_logger.LogError(ex, "Dexcom fetching failed or received incorrect format.");
if (_settings.IsDebugMode)
_debug.ShowDebugAlert(ex, "Dexcom result fetch");
logger.LogError(ex, "Dexcom fetching failed or received incorrect format.");
if (settings.IsDebugMode)
debug.ShowDebugAlert(ex, "Dexcom result fetch");

glucoseResult = GlucoseResult.Default;
glucoseResult.SetDefault();
}

return glucoseResult;
}

private GlucoseResult MapToResult(DexcomResult data)
private void MapToResult(DexcomResult data)
{
GlucoseResult glucoseResult = new();

var unixTime = string.Join("", data.ST.Where(char.IsDigit));
var trend = data.Trend;

glucoseResult.SetGlucoseValues(data.Value, _settings);
glucoseResult.SetGlucoseValues(data.Value);
glucoseResult.SetDateTimeUtc(!string.IsNullOrWhiteSpace(unixTime) ? DateTimeOffset.FromUnixTimeMilliseconds(long.Parse(unixTime)).UtcDateTime : DateTime.MinValue);
glucoseResult.SetTrend(trend.GetTrend());

return glucoseResult;
}

private async Task<string> GetApiResponse(string sessionId)
{
var url = _urlBuilder.BuildDexComGlucoseValueUrl(sessionId);
var result = await _externalAdapter.PostApiResponseAsync(url);
var url = urlBuilder.BuildDexComGlucoseValueUrl(sessionId);
var result = await externalAdapter.PostApiResponseAsync(url);
return result;
}

Expand All @@ -77,19 +63,19 @@ private async Task<string> GetSessionId(string accountId)
{
accountId,
applicationId = "d8665ade-9673-4e27-9ff6-92db4ce13d13",
password = _settings.DexcomPassword
password = settings.DexcomPassword
});

var sessionUrl = _urlBuilder.BuildDexComSessionUrl();
var sessionUrl = urlBuilder.BuildDexComSessionUrl();

var result = await _externalAdapter.PostApiResponseAsync(sessionUrl, sessionIdRequestJson);
var result = await externalAdapter.PostApiResponseAsync(sessionUrl, sessionIdRequestJson);
var sessionId = result.Replace("\"", "");

if (IsValidId(sessionId))
_debug.AddDebugText("Got a valid session id");
debug.AddDebugText("Got a valid session id");
else
{
_debug.AddDebugText("Invalid session id");
debug.AddDebugText("Invalid session id");
throw new InvalidOperationException("Invalid session id");
}

Expand All @@ -100,21 +86,21 @@ private async Task<string> GetAccountId()
{
var accountIdRequestJson = JsonSerializer.Serialize(new
{
accountName = _settings.DexcomUsername,
accountName = settings.DexcomUsername,
applicationId = "d8665ade-9673-4e27-9ff6-92db4ce13d13",
password = _settings.DexcomPassword
password = settings.DexcomPassword
});

var accountUrl = _urlBuilder.BuildDexComAccountIdUrl();
var accountUrl = urlBuilder.BuildDexComAccountIdUrl();

var result = await _externalAdapter.PostApiResponseAsync(accountUrl, accountIdRequestJson);
var result = await externalAdapter.PostApiResponseAsync(accountUrl, accountIdRequestJson);
var accountId = result.Replace("\"", "");

if (IsValidId(accountId))
_debug.AddDebugText("Got a valid account id");
debug.AddDebugText("Got a valid account id");
else
{
_debug.AddDebugText("Invalid account id");
debug.AddDebugText("Invalid account id");
throw new InvalidOperationException("Invalid account id");
}

Expand Down
11 changes: 4 additions & 7 deletions GlucoseTray.Domain/FetchResults/GlucoseFetchService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace GlucoseTray.Domain.FetchResults;

public interface IGlucoseFetchService
{
Task<GlucoseResult> GetLatestReadingsAsync();
Task GetLatestReadingsAsync();
}

public class GlucoseFetchService : IGlucoseFetchService
Expand All @@ -26,18 +26,17 @@ public GlucoseFetchService(ISettingsProxy options, ILogger<GlucoseFetchService>
_nightscoutService = nightscoutService;
}

public async Task<GlucoseResult> GetLatestReadingsAsync()
public async Task GetLatestReadingsAsync()
{
var fetchResult = new GlucoseResult();
try
{
switch (_options.FetchMethod)
{
case FetchMethod.DexcomShare:
fetchResult = await _dexcomService.GetLatestReadingAsync();
await _dexcomService.GetLatestReadingAsync();
break;
case FetchMethod.NightscoutApi:
fetchResult = await _nightscoutService.GetLatestReadingAsync();
await _nightscoutService.GetLatestReadingAsync();
break;
default:
_logger.LogError("Invalid fetch method specified.");
Expand All @@ -48,7 +47,5 @@ public async Task<GlucoseResult> GetLatestReadingsAsync()
{
_logger.LogError(ex, "Failed to get data.");
}

return fetchResult;
}
}
56 changes: 22 additions & 34 deletions GlucoseTray.Domain/FetchResults/Nightscout/NightscoutService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,63 +8,51 @@ namespace GlucoseTray.Domain.FetchResults.Nightscout;

public interface INightscoutService
{
Task<GlucoseResult> GetLatestReadingAsync();
Task GetLatestReadingAsync();
}

public class NightscoutService(ISettingsProxy settings, ILogger<NightscoutService> logger, UrlAssembler urlBuilder, IExternalCommunicationAdapter externalAdapter, DebugService debug) : INightscoutService
public class NightscoutService(ISettingsProxy settings, ILogger<NightscoutService> logger, UrlAssembler urlBuilder, IExternalCommunicationAdapter externalAdapter, DebugService debug, GlucoseResult glucoseResult) : INightscoutService
{
private readonly ISettingsProxy _settings = settings;
private readonly ILogger _logger = logger;
private readonly UrlAssembler _urlBuilder = urlBuilder;
private readonly IExternalCommunicationAdapter _externalAdapter = externalAdapter;
private readonly DebugService _debug = debug;

public async Task<GlucoseResult> GetLatestReadingAsync()
public async Task GetLatestReadingAsync()
{
_debug.ClearDebugText();
_debug.AddDebugText("Starting Nightscout Fetch");
_debug.AddDebugText(!string.IsNullOrWhiteSpace(_settings.AccessToken) ? "Using access token." : "No access token.");

GlucoseResult result = new();
debug.ClearDebugText();
debug.AddDebugText("Starting Nightscout Fetch");
debug.AddDebugText(!string.IsNullOrWhiteSpace(settings.AccessToken) ? "Using access token." : "No access token.");

try
{
var response = await GetApiResponse();

_debug.AddDebugText("Attempting to deserialize");
debug.AddDebugText("Attempting to deserialize");
var record = JsonSerializer.Deserialize<List<NightScoutResult>>(response)!.Last();
_debug.AddDebugText("Deserialized.");
debug.AddDebugText("Deserialized.");

result = MapToResult(record);
MapToResult(record);
}
catch (Exception ex)
{
_logger.LogError(ex, "Nightscout fetching failed or received incorrect format.");
if (_settings.IsDebugMode)
_debug.ShowDebugAlert(ex, "Nightscout result fetch");
}
logger.LogError(ex, "Nightscout fetching failed or received incorrect format.");
if (settings.IsDebugMode)
debug.ShowDebugAlert(ex, "Nightscout result fetch");

return result;
glucoseResult.SetDefault();
}
}

private GlucoseResult MapToResult(NightScoutResult data)
private void MapToResult(NightScoutResult data)
{
GlucoseResult result = new();

result.SetDateTimeUtc(!string.IsNullOrEmpty(data.DateString) ? DateTime.Parse(data.DateString).ToUniversalTime() : DateTimeOffset.FromUnixTimeMilliseconds(data.Date).UtcDateTime);
result.SetTrend(data.Direction.GetTrend());
result.SetGlucoseValues(data.Sgv, _settings);
glucoseResult.SetDateTimeUtc(!string.IsNullOrEmpty(data.DateString) ? DateTime.Parse(data.DateString).ToUniversalTime() : DateTimeOffset.FromUnixTimeMilliseconds(data.Date).UtcDateTime);
glucoseResult.SetTrend(data.Direction.GetTrend());
glucoseResult.SetGlucoseValues(data.Sgv);

if (result.Trend == TrendResult.Unknown)
_logger.LogWarning("Un-expected value for direction/Trend {Direction}", data.Direction);

return result;
if (glucoseResult.Trend == TrendResult.Unknown)
logger.LogWarning("Un-expected value for direction/Trend {Direction}", data.Direction);
}

private async Task<string> GetApiResponse()
{
var url = _urlBuilder.BuildNightscoutUrl();
var result = await _externalAdapter.GetApiResponseAsync(url);
var url = urlBuilder.BuildNightscoutUrl();
var result = await externalAdapter.GetApiResponseAsync(url);
return result;
}
}
Loading

0 comments on commit 9bd6bd1

Please sign in to comment.