Delete .Net Capstone Project/MedplumFhirServicevv1.cs
This commit is contained in:
parent
c371cf7972
commit
56b493e7be
@ -1,317 +0,0 @@
|
|||||||
using ClinicalInsightsPro.API.Data;
|
|
||||||
using ClinicalInsightsPro.API.DTOs;
|
|
||||||
using ClinicalInsightsPro.API.Services.Interfaces;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace ClinicalInsightsPro.API.Services;
|
|
||||||
|
|
||||||
public class MedplumFhirService : IMedplumFhirService
|
|
||||||
{
|
|
||||||
private readonly ApplicationDbContext _db;
|
|
||||||
private readonly ILogger<MedplumFhirService> _logger;
|
|
||||||
|
|
||||||
public MedplumFhirService(
|
|
||||||
ApplicationDbContext db,
|
|
||||||
ILogger<MedplumFhirService> logger)
|
|
||||||
{
|
|
||||||
_db = db;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<List<JsonElement>> GetMedplumResourcesAsync(string resourceType, string? patientId = null)
|
|
||||||
{
|
|
||||||
// Offline/demo mode has normalized tables, not the raw Medplum store.
|
|
||||||
// The unified Medplum browser is intentionally live-only so the UI
|
|
||||||
// always shows actual FHIR JSON when the feature is enabled.
|
|
||||||
throw new InvalidOperationException(
|
|
||||||
"The Medplum resource browser requires Fhir:UseLiveMedplum=true. " +
|
|
||||||
"Set MEDPLUM_USE_LIVE=true and configure MEDPLUM_BASE_URL, " +
|
|
||||||
"MEDPLUM_TOKEN_URL, MEDPLUM_CLIENT_ID and MEDPLUM_CLIENT_SECRET.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<PatientSummaryDto>> GetPatientListAsync()
|
|
||||||
{
|
|
||||||
var patients = await _db.Patient
|
|
||||||
.AsNoTracking()
|
|
||||||
.OrderBy(x => x.GivenName)
|
|
||||||
.ThenBy(x => x.FamilyName)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
return patients.Select(p => new PatientSummaryDto
|
|
||||||
{
|
|
||||||
Id = p.Id,
|
|
||||||
FullName = $"{p.GivenName} {p.FamilyName}",
|
|
||||||
Age = CalculateAge(p.BirthDate),
|
|
||||||
Gender = p.Gender,
|
|
||||||
MedicalRecordNumber = p.MedicalRecordNumber
|
|
||||||
}).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<PatientChartDto> GetPatientChartAsync(string patientId)
|
|
||||||
{
|
|
||||||
var patient = await _db.Patient
|
|
||||||
.AsNoTracking()
|
|
||||||
.FirstOrDefaultAsync(p => p.Id == patientId);
|
|
||||||
|
|
||||||
if (patient == null)
|
|
||||||
{
|
|
||||||
throw new KeyNotFoundException(
|
|
||||||
$"Patient '{patientId}' was not found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var conditions = await _db.Condition
|
|
||||||
.AsNoTracking()
|
|
||||||
.Where(x => x.PatientId == patientId)
|
|
||||||
.OrderByDescending(x => x.RecordedDate)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
var observations = await _db.Observation
|
|
||||||
.AsNoTracking()
|
|
||||||
.Where(x => x.PatientId == patientId)
|
|
||||||
.OrderByDescending(x => x.EffectiveDateTime)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
var allergies = await _db.AllergyIntolerance
|
|
||||||
.AsNoTracking()
|
|
||||||
.Where(x => x.PatientId == patientId)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
var medications = await _db.MedicationRequest
|
|
||||||
.AsNoTracking()
|
|
||||||
.Where(x => x.PatientId == patientId)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
var encounters = await _db.Encounter
|
|
||||||
.AsNoTracking()
|
|
||||||
.Where(x => x.PatientId == patientId)
|
|
||||||
.OrderByDescending(x => x.PeriodStart)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
_logger.LogInformation(
|
|
||||||
"Retrieved chart for patient {PatientId}",
|
|
||||||
patientId);
|
|
||||||
|
|
||||||
return new PatientChartDto
|
|
||||||
{
|
|
||||||
PatientId = patient.Id,
|
|
||||||
|
|
||||||
FullName = $"{patient.GivenName} {patient.FamilyName}",
|
|
||||||
|
|
||||||
Age = CalculateAge(patient.BirthDate),
|
|
||||||
|
|
||||||
MedicalRecordNumber = patient.MedicalRecordNumber,
|
|
||||||
|
|
||||||
IdentifierSystem = patient.IdentifierSystem,
|
|
||||||
IdentifierValue = patient.IdentifierValue,
|
|
||||||
|
|
||||||
NameUse = patient.NameUse,
|
|
||||||
Prefix = patient.Prefix,
|
|
||||||
|
|
||||||
GivenName = patient.GivenName,
|
|
||||||
FamilyName = patient.FamilyName,
|
|
||||||
|
|
||||||
TelecomSystem = patient.TelecomSystem,
|
|
||||||
TelecomUse = patient.TelecomUse,
|
|
||||||
TelecomValue = patient.TelecomValue,
|
|
||||||
|
|
||||||
Gender = patient.Gender,
|
|
||||||
|
|
||||||
BloodGroup = patient.BloodGroup,
|
|
||||||
Email = patient.Email,
|
|
||||||
|
|
||||||
BirthDate = patient.BirthDate,
|
|
||||||
|
|
||||||
AddressUse = patient.AddressUse,
|
|
||||||
AddressType = patient.AddressType,
|
|
||||||
AddressLine = patient.AddressLine,
|
|
||||||
|
|
||||||
City = patient.City,
|
|
||||||
State = patient.State,
|
|
||||||
PostalCode = patient.PostalCode,
|
|
||||||
|
|
||||||
MaritalStatusSystem = patient.MaritalStatusSystem,
|
|
||||||
MaritalStatusCode = patient.MaritalStatusCode,
|
|
||||||
MaritalStatusDisplay = patient.MaritalStatusDisplay,
|
|
||||||
|
|
||||||
ContactNameUse = patient.ContactNameUse,
|
|
||||||
ContactPrefix = patient.ContactPrefix,
|
|
||||||
|
|
||||||
ContactTelecomSystem = patient.ContactTelecomSystem,
|
|
||||||
ContactTelecomUse = patient.ContactTelecomUse,
|
|
||||||
ContactTelecomValue = patient.ContactTelecomValue,
|
|
||||||
|
|
||||||
ContactAddressUse = patient.ContactAddressUse,
|
|
||||||
ContactAddressType = patient.ContactAddressType,
|
|
||||||
ContactAddressLine = patient.ContactAddressLine,
|
|
||||||
|
|
||||||
ContactCity = patient.ContactCity,
|
|
||||||
ContactState = patient.ContactState,
|
|
||||||
ContactPostalCode = patient.ContactPostalCode,
|
|
||||||
|
|
||||||
ContactGender = patient.ContactGender,
|
|
||||||
ContactOrganizationId = patient.ContactOrganizationId,
|
|
||||||
|
|
||||||
ContactPeriodStart = patient.ContactPeriodStart,
|
|
||||||
ContactPeriodEnd = patient.ContactPeriodEnd,
|
|
||||||
|
|
||||||
ManagingOrganizationId = patient.ManagingOrganizationId,
|
|
||||||
|
|
||||||
PhotoContentType = patient.PhotoContentType,
|
|
||||||
PhotoUrl = patient.PhotoUrl,
|
|
||||||
PhotoTitle = patient.PhotoTitle,
|
|
||||||
|
|
||||||
GeneralPractitionerId = patient.GeneralPractitionerId,
|
|
||||||
|
|
||||||
Conditions = conditions.Select(c => new ChartConditionDto
|
|
||||||
{
|
|
||||||
Id = c.Id,
|
|
||||||
Code = c.ConditionCode,
|
|
||||||
Display = c.ConditionDisplay,
|
|
||||||
ClinicalStatus = c.ClinicalStatusDisplay,
|
|
||||||
VerificationStatus = c.VerificationStatusDisplay,
|
|
||||||
Severity = c.SeverityDisplay,
|
|
||||||
OnsetDate = c.OnsetDateTime,
|
|
||||||
AbatementDate = c.AbatementDateTime,
|
|
||||||
RecordedDate = c.RecordedDate
|
|
||||||
}).ToList(),
|
|
||||||
|
|
||||||
Allergies = allergies.Select(a => new ChartAllergyDto
|
|
||||||
{
|
|
||||||
Id = a.Id,
|
|
||||||
Type = a.Type,
|
|
||||||
ClinicalStatus = a.ClinicalStatus,
|
|
||||||
VerificationStatus = a.VerificationStatus,
|
|
||||||
Criticality = a.Criticality,
|
|
||||||
RecorderPractitionerId = a.RecorderPractitionerId
|
|
||||||
}).ToList(),
|
|
||||||
|
|
||||||
Medications = medications.Select(m => new ChartMedicationDto
|
|
||||||
{
|
|
||||||
Id = m.Id,
|
|
||||||
MedicationCode = m.MedicationCode,
|
|
||||||
MedicationDisplay = m.MedicationDisplay,
|
|
||||||
Status = m.Status,
|
|
||||||
Intent = m.Intent,
|
|
||||||
Priority = m.Priority
|
|
||||||
}).ToList(),
|
|
||||||
|
|
||||||
Observations = observations.Select(o => new ChartObservationDto
|
|
||||||
{
|
|
||||||
Id = o.Id,
|
|
||||||
Code = o.ObservationCode,
|
|
||||||
Display = o.ObservationDisplay,
|
|
||||||
Status = o.Status,
|
|
||||||
CategoryDisplay = o.CategoryDisplay,
|
|
||||||
InterpretationCode = o.InterpretationCode,
|
|
||||||
InterpretationDisplay = o.InterpretationDisplay,
|
|
||||||
EffectiveDate = o.EffectiveDateTime,
|
|
||||||
NoteText = o.NoteText
|
|
||||||
}).ToList(),
|
|
||||||
|
|
||||||
Encounters = encounters.Select(e => new ChartEncounterDto
|
|
||||||
{
|
|
||||||
Id = e.Id,
|
|
||||||
Status = e.Status,
|
|
||||||
ClassCode = e.ClassCode,
|
|
||||||
ClassDisplay = e.ClassDisplay,
|
|
||||||
TypeDisplay = e.TypeDisplay,
|
|
||||||
ServiceTypeDisplay = e.ServiceTypeDisplay,
|
|
||||||
PeriodStart = e.PeriodStart,
|
|
||||||
PeriodEnd = e.PeriodEnd
|
|
||||||
}).ToList()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int CalculateAge(DateTime birthDate)
|
|
||||||
{
|
|
||||||
var today = DateTime.UtcNow;
|
|
||||||
|
|
||||||
var age = today.Year - birthDate.Year;
|
|
||||||
|
|
||||||
if (birthDate.Date > today.AddYears(-age))
|
|
||||||
{
|
|
||||||
age--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return age;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Two-way sync additions ----
|
|
||||||
// This class is the OFFLINE/local implementation (used when
|
|
||||||
// Fhir:UseLiveMedplum=false) — it reads/writes only the local database
|
|
||||||
// and never talks to a real Medplum server, so there is nothing to push
|
|
||||||
// or pull here. These methods exist only so this class still satisfies
|
|
||||||
// IMedplumFhirService; they throw/no-op with a clear message instead of
|
|
||||||
// silently pretending to sync. Set MEDPLUM_USE_LIVE=true in .env to
|
|
||||||
// switch the app over to LiveMedplumFhirService, which implements the
|
|
||||||
// real sync against Medplum's FHIR API.
|
|
||||||
public Task<string> CreatePatientInMedplumAsync(Models.Patient patient)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(
|
|
||||||
"CreatePatientInMedplumAsync called while running in offline/local mode " +
|
|
||||||
"(Fhir:UseLiveMedplum=false) — patient {Id} was saved locally only, not pushed to Medplum. " +
|
|
||||||
"Set MEDPLUM_USE_LIVE=true in .env to enable Medplum sync.", patient.Id);
|
|
||||||
return Task.FromResult(string.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task UpdatePatientInMedplumAsync(Models.Patient patient)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(
|
|
||||||
"UpdatePatientInMedplumAsync called while running in offline/local mode " +
|
|
||||||
"(Fhir:UseLiveMedplum=false) — patient {Id} was updated locally only, not pushed to Medplum.", patient.Id);
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task DeletePatientInMedplumAsync(string medplumId)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(
|
|
||||||
"DeletePatientInMedplumAsync called while running in offline/local mode " +
|
|
||||||
"(Fhir:UseLiveMedplum=false) — nothing was deleted in Medplum.");
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<List<Models.Patient>> PullAllPatientsFromMedplumAsync()
|
|
||||||
{
|
|
||||||
_logger.LogWarning(
|
|
||||||
"PullAllPatientsFromMedplumAsync called while running in offline/local mode " +
|
|
||||||
"(Fhir:UseLiveMedplum=false) — returning an empty list. Set MEDPLUM_USE_LIVE=true in .env " +
|
|
||||||
"to pull real data from Medplum.");
|
|
||||||
return Task.FromResult(new List<Models.Patient>());
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Practitioner two-way sync (offline no-ops, mirrors Patient above) ----
|
|
||||||
public Task<string> CreatePractitionerInMedplumAsync(Models.Practitioner practitioner)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(
|
|
||||||
"CreatePractitionerInMedplumAsync called while running in offline/local mode " +
|
|
||||||
"(Fhir:UseLiveMedplum=false) — practitioner {Id} was saved locally only, not pushed to Medplum. " +
|
|
||||||
"Set MEDPLUM_USE_LIVE=true in .env to enable Medplum sync.", practitioner.Id);
|
|
||||||
return Task.FromResult(string.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task UpdatePractitionerInMedplumAsync(Models.Practitioner practitioner)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(
|
|
||||||
"UpdatePractitionerInMedplumAsync called while running in offline/local mode " +
|
|
||||||
"(Fhir:UseLiveMedplum=false) — practitioner {Id} was updated locally only, not pushed to Medplum.", practitioner.Id);
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task DeletePractitionerInMedplumAsync(string medplumId)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(
|
|
||||||
"DeletePractitionerInMedplumAsync called while running in offline/local mode " +
|
|
||||||
"(Fhir:UseLiveMedplum=false) — nothing was deleted in Medplum.");
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<List<Models.Practitioner>> PullAllPractitionersFromMedplumAsync()
|
|
||||||
{
|
|
||||||
_logger.LogWarning(
|
|
||||||
"PullAllPractitionersFromMedplumAsync called while running in offline/local mode " +
|
|
||||||
"(Fhir:UseLiveMedplum=false) — returning an empty list. Set MEDPLUM_USE_LIVE=true in .env " +
|
|
||||||
"to pull real data from Medplum.");
|
|
||||||
return Task.FromResult(new List<Models.Practitioner>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user