chore: necessary updates given the changes in the alert controller
This commit is contained in:
@@ -227,6 +227,13 @@ public class EsIndexerService : BackgroundService
|
||||
AlertType = evt.AlertType,
|
||||
Severity = evt.Severity,
|
||||
Status = "Open",
|
||||
Details = root.TryGetProperty("details", out var detailsElem)
|
||||
? detailsElem.GetString() ?? string.Empty
|
||||
: string.Empty,
|
||||
NarrativeSummary = root.TryGetProperty("explanation", out var explanationElem)
|
||||
&& explanationElem.TryGetProperty("narrativeSummary", out var narrativeElem)
|
||||
? narrativeElem.GetString()
|
||||
: null,
|
||||
TriggeredAt = evt.TriggeredAt
|
||||
};
|
||||
|
||||
|
||||
@@ -135,13 +135,13 @@ public class AlertsController : ControllerBase
|
||||
/// <returns>The updated alert.</returns>
|
||||
[HttpPost("api/v1/alerts/{id:guid}/acknowledge")]
|
||||
[AuthorizePermission(ClinicalPermissions.AlertsAcknowledge)]
|
||||
[ProducesResponseType(typeof(ApiResponse<ClinicalAlert>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ApiResponse<AlertResponse>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status409Conflict)]
|
||||
public async Task<IActionResult> Acknowledge(Guid id, [FromBody] AcknowledgeAlertRequest req)
|
||||
{
|
||||
var alert = await _alerts.AcknowledgeAsync(id, req);
|
||||
return Ok(ApiResponse<ClinicalAlert>.Ok(alert));
|
||||
return Ok(ApiResponse<AlertResponse>.Ok(alert));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -151,13 +151,13 @@ public class AlertsController : ControllerBase
|
||||
/// <returns>The updated alert.</returns>
|
||||
[HttpPost("api/v1/alerts/{id:guid}/resolve")]
|
||||
[AuthorizePermission(ClinicalPermissions.AlertsResolve)]
|
||||
[ProducesResponseType(typeof(ApiResponse<ClinicalAlert>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ApiResponse<AlertResponse>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status409Conflict)]
|
||||
public async Task<IActionResult> Resolve(Guid id)
|
||||
{
|
||||
var alert = await _alerts.ResolveAsync(id);
|
||||
return Ok(ApiResponse<ClinicalAlert>.Ok(alert));
|
||||
return Ok(ApiResponse<AlertResponse>.Ok(alert));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -39,6 +39,7 @@ public static class DataLakeEventParser
|
||||
AlertType : GetString(d, "alertType"),
|
||||
Severity : GetString(d, "severity"),
|
||||
Details : GetString(d, "details"),
|
||||
ExplanationJson: GetJsonObjectString(d, "explanation"),
|
||||
TriggeredAt : GetTimestampString(d, "triggeredAt"),
|
||||
KafkaPartition : partition,
|
||||
KafkaOffset : offset);
|
||||
@@ -78,6 +79,13 @@ public static class DataLakeEventParser
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetJsonObjectString(JsonElement d, string name)
|
||||
{
|
||||
if (!TryGetProperty(d, name, out var prop) || prop.ValueKind != JsonValueKind.Object)
|
||||
return "";
|
||||
return prop.GetRawText();
|
||||
}
|
||||
|
||||
private static string GetString(JsonElement d, string primary, string? alternate = null)
|
||||
{
|
||||
if (TryGetProperty(d, primary, out var prop))
|
||||
|
||||
@@ -49,6 +49,7 @@ public static class ParquetFileBuilder
|
||||
new DataField<string>("alert_type"),
|
||||
new DataField<string>("severity"),
|
||||
new DataField<string>("details"),
|
||||
new DataField<string>("explanation_json"),
|
||||
new DataField<string>("triggered_at"),
|
||||
new DataField<int>("kafka_partition"),
|
||||
new DataField<long>("kafka_offset")
|
||||
@@ -65,9 +66,10 @@ public static class ParquetFileBuilder
|
||||
await rg.WriteColumnAsync(new DataColumn(f[3], rows.Select(r => r.AlertType).ToArray()));
|
||||
await rg.WriteColumnAsync(new DataColumn(f[4], rows.Select(r => r.Severity).ToArray()));
|
||||
await rg.WriteColumnAsync(new DataColumn(f[5], rows.Select(r => r.Details).ToArray()));
|
||||
await rg.WriteColumnAsync(new DataColumn(f[6], rows.Select(r => r.TriggeredAt).ToArray()));
|
||||
await rg.WriteColumnAsync(new DataColumn(f[7], rows.Select(r => r.KafkaPartition).ToArray()));
|
||||
await rg.WriteColumnAsync(new DataColumn(f[8], rows.Select(r => r.KafkaOffset).ToArray()));
|
||||
await rg.WriteColumnAsync(new DataColumn(f[6], rows.Select(r => r.ExplanationJson).ToArray()));
|
||||
await rg.WriteColumnAsync(new DataColumn(f[7], rows.Select(r => r.TriggeredAt).ToArray()));
|
||||
await rg.WriteColumnAsync(new DataColumn(f[8], rows.Select(r => r.KafkaPartition).ToArray()));
|
||||
await rg.WriteColumnAsync(new DataColumn(f[9], rows.Select(r => r.KafkaOffset).ToArray()));
|
||||
}
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
@@ -7,5 +7,7 @@ public class ClinicalAlertDocument
|
||||
public string AlertType { get; set; } = null!;
|
||||
public string Severity { get; set; } = null!;
|
||||
public string Status { get; set; } = null!;
|
||||
public string Details { get; set; } = string.Empty;
|
||||
public string? NarrativeSummary { get; set; }
|
||||
public DateTimeOffset TriggeredAt { get; set; }
|
||||
}
|
||||
@@ -5,6 +5,7 @@ public sealed record AlertRow(
|
||||
string AlertType,
|
||||
string Severity,
|
||||
string Details,
|
||||
string ExplanationJson,
|
||||
string TriggeredAt,
|
||||
int KafkaPartition,
|
||||
long KafkaOffset
|
||||
|
||||
@@ -85,7 +85,7 @@ public class AlertService : IAlertService
|
||||
return AlertResponseMapper.ToResponse(alert);
|
||||
}
|
||||
|
||||
public async Task<ClinicalAlert> AcknowledgeAsync(Guid id, AcknowledgeAlertRequest req)
|
||||
public async Task<AlertResponse> AcknowledgeAsync(Guid id, AcknowledgeAlertRequest req)
|
||||
{
|
||||
if (!_currentUser.IsAuthenticated)
|
||||
throw new ValidationException("Authentication required.", "AUTH_REQUIRED");
|
||||
@@ -164,7 +164,7 @@ public class AlertService : IAlertService
|
||||
reason: acknowledgmentNote);
|
||||
}
|
||||
|
||||
return alert;
|
||||
return AlertResponseMapper.ToResponse(alert);
|
||||
}
|
||||
|
||||
public async Task<AlertFeedback> SubmitFeedbackAsync(
|
||||
@@ -234,7 +234,7 @@ public class AlertService : IAlertService
|
||||
return overrideMinutes ?? defaultWindowMinutes;
|
||||
}
|
||||
|
||||
public async Task<ClinicalAlert> ResolveAsync(Guid id)
|
||||
public async Task<AlertResponse> ResolveAsync(Guid id)
|
||||
{
|
||||
var alert = await _db.ClinicalAlerts.FindAsync(id);
|
||||
if (alert is null)
|
||||
@@ -256,7 +256,7 @@ public class AlertService : IAlertService
|
||||
previousValue: new { status = AlertStatus.Acknowledged.ToDbString() },
|
||||
newValue: new { status = alert.Status.ToDbString() });
|
||||
|
||||
return alert;
|
||||
return AlertResponseMapper.ToResponse(alert);
|
||||
}
|
||||
|
||||
public async Task ApplySyncedAcknowledgmentAsync(
|
||||
|
||||
@@ -6,6 +6,11 @@ using VigilCare.ClinicalContracts.Sync;
|
||||
|
||||
public class ClinicalSyncBatchProcessor
|
||||
{
|
||||
private static readonly JsonSerializerOptions ExplanationJsonOptions = new()
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
|
||||
private readonly AppDbContext _db;
|
||||
private readonly IObservationService _observations;
|
||||
private readonly IAlertService _alerts;
|
||||
@@ -163,6 +168,7 @@ public class ClinicalSyncBatchProcessor
|
||||
AlertType = AlertTypeExtensions.FromDbString(alert.AlertType),
|
||||
Severity = AlertSeverityExtensions.FromDbString(alert.Severity),
|
||||
Details = alert.Details,
|
||||
Explanation = ParseExplanation(alert.ExplanationJson),
|
||||
Status = AlertStatus.Open,
|
||||
TriggeredAt = alert.GeneratedAt
|
||||
};
|
||||
@@ -172,7 +178,7 @@ public class ClinicalSyncBatchProcessor
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Topic = "alert.generated",
|
||||
Payload = JsonSerializer.Serialize(new
|
||||
Payload = ClinicalAlertFactory.SerializeOutboxPayload(new
|
||||
{
|
||||
alertId = clinicalAlert.Id,
|
||||
encounterId = alert.EncounterId,
|
||||
@@ -180,6 +186,7 @@ public class ClinicalSyncBatchProcessor
|
||||
alertType = alert.AlertType,
|
||||
severity = alert.Severity,
|
||||
details = alert.Details,
|
||||
explanation = clinicalAlert.Explanation,
|
||||
syncedFromGateway = true,
|
||||
triggeredAt = alert.GeneratedAt,
|
||||
partitionKey = alert.EncounterId.ToString()
|
||||
@@ -231,4 +238,9 @@ public class ClinicalSyncBatchProcessor
|
||||
_db.ClinicalSyncConflicts.Add(new ClinicalSyncConflict(batchId, clientRef, itemType, reason));
|
||||
await _db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
private static AlertExplanation? ParseExplanation(string? explanationJson) =>
|
||||
string.IsNullOrWhiteSpace(explanationJson)
|
||||
? null
|
||||
: JsonSerializer.Deserialize<AlertExplanation>(explanationJson, ExplanationJsonOptions);
|
||||
}
|
||||
@@ -8,9 +8,9 @@ public interface IAlertService
|
||||
|
||||
Task<AlertResponse> GetByIdAsync(Guid id);
|
||||
|
||||
Task<ClinicalAlert> AcknowledgeAsync(Guid id, AcknowledgeAlertRequest req);
|
||||
Task<AlertResponse> AcknowledgeAsync(Guid id, AcknowledgeAlertRequest req);
|
||||
|
||||
Task<ClinicalAlert> ResolveAsync(Guid id);
|
||||
Task<AlertResponse> ResolveAsync(Guid id);
|
||||
Task ApplySyncedAcknowledgmentAsync(Guid alertId, SyncedAlertAcknowledgment ack, CancellationToken ct);
|
||||
Task ApplySyncedResolutionAsync(Guid alertId, SyncedAlertResolution resolve, CancellationToken ct);
|
||||
Task<AlertFeedback> SubmitFeedbackAsync(Guid alertId, AlertFeedbackType type, string? comment);
|
||||
|
||||
Reference in New Issue
Block a user