Files
vigilcare-records/VigilCareRecordsAPI/Services/DigitizationHistoryService.cs
T

122 lines
5.1 KiB
C#

using Microsoft.EntityFrameworkCore;
public class DigitizationHistoryService : IDigitizationHistoryService
{
private readonly AppDbContext _db;
public DigitizationHistoryService(AppDbContext db)
{
_db = db;
}
public async Task<PatientDigitizationHistoryResponse> GetPatientHistoryAsync(Guid patientId)
{
// Verify the patient exists (has at least one batch)
var hasBatches = await _db.DigitizationBatches
.AnyAsync(b => b.PatientId == patientId);
if (!hasBatches)
throw new NotFoundException(
$"No digitization history found for patient {patientId}.",
"PATIENT_HISTORY_NOT_FOUND");
// Load all batches for this patient with their relationships
var batches = await _db.DigitizationBatches
.Include(b => b.DraftObservations)
.Include(b => b.Events)
.ThenInclude(e => e.Actor)
.Include(b => b.EnteredByUser)
.Include(b => b.VerifiedByUser)
.Include(b => b.ApprovedByUser)
.Where(b => b.PatientId == patientId)
.OrderByDescending(b => b.CreatedAt)
.ToListAsync();
// For each promoted batch, count live vs superseded observations
var promotedBatchIds = batches
.Where(b => b.Status == BatchStatus.Promoted)
.Select(b => b.Id)
.ToList();
var liveObservationCounts = await _db.LiveObservations
.Where(o => promotedBatchIds.Contains(o.SourceBatchId))
.GroupBy(o => new { o.SourceBatchId, o.IsSuperseded })
.Select(g => new
{
g.Key.SourceBatchId,
g.Key.IsSuperseded,
Count = g.Count()
})
.ToListAsync();
// Build a lookup: batchId -> (activeCount, supersededCount)
var obsCountLookup = promotedBatchIds.ToDictionary(
id => id,
id =>
{
var active = liveObservationCounts
.FirstOrDefault(x => x.SourceBatchId == id && !x.IsSuperseded)?.Count ?? 0;
var superseded = liveObservationCounts
.FirstOrDefault(x => x.SourceBatchId == id && x.IsSuperseded)?.Count ?? 0;
return (Active: active, Superseded: superseded);
});
// Build a lookup for "has been superseded" — batches that appear as
// SupersedesBatchId on another batch that reached Promoted
var supersededByLookup = await _db.DigitizationBatches
.Where(b => b.SupersedesBatchId.HasValue
&& b.Status == BatchStatus.Promoted
&& promotedBatchIds.Contains(b.SupersedesBatchId.Value))
.ToDictionaryAsync(
b => b.SupersedesBatchId!.Value,
b => b.Id);
var entries = batches.Select(b =>
{
var counts = obsCountLookup.GetValueOrDefault(b.Id, (Active: 0, Superseded: 0));
var hasBeenSuperseded = supersededByLookup.ContainsKey(b.Id);
supersededByLookup.TryGetValue(b.Id, out var supersededByBatchId);
return new DigitizationHistoryEntry(
BatchId: b.Id,
Status: b.Status.ToDbString(),
BatchType: b.BatchType.ToDbString(),
Track: b.Track.ToDbString(),
SupersedesBatchId: b.SupersedesBatchId,
IsCorrection: b.SupersedesBatchId.HasValue,
HasBeenSuperseded: hasBeenSuperseded,
SupersededByBatchId: hasBeenSuperseded ? supersededByBatchId : null,
DraftObservationCount: b.DraftObservations.Count,
LiveObservationCount: counts.Active,
SupersededObservationCount: counts.Superseded,
CreatedAt: b.CreatedAt,
PromotedAt: b.PromotedAt,
PromotionEncounterId: b.PromotionEncounterId,
EnteredByUserId: b.EnteredByUserId,
EnteredByUserName: b.EnteredByUser?.FullName,
VerifiedByUserId: b.VerifiedByUserId,
VerifiedByUserName: b.VerifiedByUser?.FullName,
ApprovedByUserId: b.ApprovedByUserId,
ApprovedByUserName: b.ApprovedByUser?.FullName,
AuditTrail: b.Events
.OrderBy(e => e.OccurredAt)
.Select(e => new DigitizationEventSummary(
e.EventType.ToDbString(),
e.OccurredAt,
e.ActorUserId,
e.Actor?.FullName ?? "Unknown",
e.MetadataJson))
.ToList()
);
}).ToList();
return new PatientDigitizationHistoryResponse(
PatientId: patientId,
TotalBatches: entries.Count,
PromotedBatches: entries.Count(e => e.Status == "PROMOTED"),
SupersededBatches: entries.Count(e => e.HasBeenSuperseded),
PendingBatches: entries.Count(e => e.Status != "PROMOTED" && e.Status != "REJECTED"),
Entries: entries
);
}
}