Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fhir server integration and patient searching/opening #23

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Common/Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Hl7.Fhir.STU3" Version="0.96.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.1.1" />
</ItemGroup>

<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Mvc.ViewFeatures">
<HintPath>..\..\..\..\..\..\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.aspnetcore.mvc.viewfeatures\2.1.1\lib\netstandard2.0\Microsoft.AspNetCore.Mvc.ViewFeatures.dll</HintPath>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this will build as it's an absolute reference on your computer. Could you add that reference as a NuGet package instead? Like what you're doing on line 8 above.

</Reference>
</ItemGroup>

</Project>
26 changes: 24 additions & 2 deletions Common/Model/ClientModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,45 @@
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Rendering;
using Hl7.Fhir.Model;

namespace FHIRcastSandbox.Model {
public class ClientModel : ModelBase {
public ClientModel()
{
ActiveSubscriptions = new List<Subscription>();
SubscriptionsToHub = new List<Subscription>();
PatientSearchOptions = new Dictionary<string, string>();
SearchPatients = new SelectList(new List<SelectListItem>());
}
public string UserIdentifier { get; set; }
public string PatientIdentifier { get; set; }
public string PatientIdIssuer { get; set; }
public string AccessionNumber { get; set; }

public string AccessionNumberGroup { get; set; }
public string StudyId { get; set; }

public string Event { get; set; }
public string Topic { get; set; }
public List<Subscription> ActiveSubscriptions { get; set; }
public List<Subscription> SubscriptionsToHub { get; set; }

//Patient Info
public Patient Patient { get; set; }
public string PatientName { get; set; }
public string PatientDOB { get; set; }
public string PatientOpenErrorDiv { get; set; }

public string SelectedPatientID { get; set; }
public IEnumerable<SelectListItem> SearchPatients { get; set; }
public Dictionary<string, string> PatientSearchOptions { get; set; }

//Study Info
public string StudyId { get; set; }
public string AccessionNumber { get; set; }
public string StudyOpenErrorDiv { get; set; }

//FHIR Server Info
public string FHIRServer { get; set; }
}
}
240 changes: 222 additions & 18 deletions WebSubClient/Controllers/WebSubClientController.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
using FHIRcastSandbox.Model;
using Hl7.Fhir.Model;
using Hl7.Fhir.Rest;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using FHIRcastSandbox.Model;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace FHIRcastSandbox.Controllers {
namespace FHIRcastSandbox.Controllers
{
[Route("")]
public class HomeController : Controller {
public IActionResult Index() {
Expand All @@ -23,34 +27,58 @@ public IActionResult Index() {
[Route("client")]
public class WebSubClientController : Controller {

private readonly ILogger<WebSubClientController> logger;


#region Constructors
public WebSubClientController(ILogger<WebSubClientController> logger) {
this.logger = logger;
this.UID = Guid.NewGuid().ToString("n");

if (internalModel == null)
{
internalModel = new ClientModel();
internalModel.FHIRServer = DEFAULT_FHIR_SERVER;
createFHIRClient();
}
}

private void createFHIRClient(string fhirServer = DEFAULT_FHIR_SERVER)
{
client = new FhirClient(fhirServer);
client.PreferredFormat = ResourceFormat.Json;
}


#endregion

#region Properties
public static ClientModel internalModel;

private static Dictionary<string, Subscription> pendingSubs = new Dictionary<string, Subscription>();
private static Dictionary<string, Subscription> activeSubs = new Dictionary<string, Subscription>();
private static Dictionary<string, Model.Subscription> pendingSubs = new Dictionary<string, Model.Subscription>();
private static Dictionary<string, Model.Subscription> activeSubs = new Dictionary<string, Model.Subscription>();

private readonly ILogger<WebSubClientController> logger;
private static FhirClient client;
private static Patient _patient;
private static ImagingStudy _study;
const string DEFAULT_FHIR_SERVER = "http://test.fhir.org/r3";
private const string VIEW_NAME = "WebSubClient";

public string UID { get; set; }
#endregion

[HttpGet]
public IActionResult Get() => View("WebSubClient", new ClientModel());
public IActionResult Get()
{
return View(VIEW_NAME, internalModel);
}

#region Client Events
public IActionResult Refresh() {
if (internalModel == null) { internalModel = new ClientModel(); }

internalModel.ActiveSubscriptions = activeSubs.Values.ToList();

return View("WebSubClient", internalModel);
return View(VIEW_NAME, internalModel);
}

/// <summary>
Expand All @@ -68,7 +96,183 @@ public IActionResult Post([FromForm] ClientModel model) {
//var response = httpClient.PostAsync(this.Request.Scheme + "://" + this.Request.Host + "/api/hub/notify", new StringContent(JsonConvert.SerializeObject(model), Encoding.UTF8, "application/json")).Result;
var response = httpClient.PostAsync(this.Request.Scheme + "://localhost:5000/api/hub/notify", new StringContent(JsonConvert.SerializeObject(model), Encoding.UTF8, "application/json")).Result;

return View("WebSubClient", model);
return View(VIEW_NAME, model);
}

[Route("searchPatients")]
[HttpPost]
public IActionResult SearchPatients(string patientID, string patientName)
{
SearchParams pars = new SearchParams();
if (patientID != null) { pars.Add("_id", patientID); }
if (patientName != null) { pars.Add("name", patientName); }

Bundle bundle = client.Search<Patient>(pars);

List<SelectListItem> patients = new List<SelectListItem>();
foreach (Bundle.EntryComponent entry in bundle.Entry)
{
Patient pat = (Patient)entry.Resource;
patients.Add(new SelectListItem
{
Value = pat.Id,
Text = pat.Name[0].ToString()
});
}

internalModel.SearchPatients = new SelectList(patients, "Value", "Text");

return View(VIEW_NAME, internalModel);
}

[Route("openPatient")]
[HttpPost]
public IActionResult OpenPatient(string patientSelect)
{
if (internalModel == null) { internalModel = new ClientModel(); }
if (patientSelect != null)
{
_patient = GetPatient(patientSelect);
if (_patient != null)
{
_study = null;
ClearPatientInfo();
return UpdateClientModel();
}
}
else
{
internalModel.PatientOpenErrorDiv = "<div><p>No patient ID given.</p></div>";
}

return View(VIEW_NAME, internalModel);
}

[Route("openStudy")]
[HttpPost]
public IActionResult OpenStudy(string studyID)
{
if (internalModel == null) { internalModel = new ClientModel(); }
if (studyID != null)
{
_study = GetStudy(studyID);
if (_study != null)
{
ClearStudyInfo();
ClearPatientInfo();
return UpdateClientModel();
}
else
{
internalModel.StudyOpenErrorDiv = "<div><p>No study ID given.</p></div>";
}
}

return View(VIEW_NAME, internalModel);
}

[Route("saveSettings")]
[HttpPost]
public IActionResult SaveSettings(string fhirServer)
{
//return PartialView("ErrorModal");
if (fhirServer != internalModel.FHIRServer)

{
internalModel.FHIRServer = fhirServer;
createFHIRClient(internalModel.FHIRServer);
}

return View(VIEW_NAME, internalModel);
}

private IActionResult UpdateClientModel()
{
//Study is nothing just use patient
//Otherwise get patient from study
if (_study != null)
{
_patient = GetPatient("", _study.Patient.Reference);

internalModel.AccessionNumber = (_study.Accession != null) ? _study.Accession.Value : "";
internalModel.StudyId = _study.Uid;
}
else
{
ClearStudyInfo();
}

if (_patient == null)
{
ClearPatientInfo();
}
else
{
internalModel.PatientName = $"{_patient.Name[0].Family}, {_patient.Name[0].Given.FirstOrDefault()}";
internalModel.PatientDOB = _patient.BirthDate;
}


internalModel.Patient = _patient;
return View(VIEW_NAME, internalModel);
}

private void ClearPatientInfo()
{
internalModel.PatientName = "";
internalModel.PatientDOB = "";
internalModel.PatientOpenErrorDiv = "";
}

private void ClearStudyInfo()
{
internalModel.StudyId = "";
internalModel.AccessionNumber = "";
internalModel.StudyOpenErrorDiv = "";
}

private Patient GetPatient(string id, string patientURI = "")
{
Uri uri = new Uri(client.Endpoint + "Patient/" + id);

try
{
if (patientURI.Length > 0) { return client.Read<Patient>(patientURI); }
else { return client.Read<Patient>(uri); }
}
catch (FhirOperationException ex)
{
foreach (var item in ex.Outcome.Children)
{
Narrative nar = item as Narrative;
if (nar != null)
{
internalModel.PatientOpenErrorDiv = nar.Div;
}
}
return null;
}
}

private ImagingStudy GetStudy(string id)
{
Uri uri = new Uri(client.Endpoint + "ImagingStudy/" + id);
try
{
return client.Read<ImagingStudy>(uri);
}
catch (FhirOperationException ex)
{
foreach (var item in ex.Outcome.Children)
{
Narrative nar = item as Narrative;
if (nar != null)
{
internalModel.StudyOpenErrorDiv = nar.Div;
}
}
return null;
}
}
#endregion

Expand All @@ -87,7 +291,7 @@ public IActionResult Get(string subscriptionId, [FromQuery] SubscriptionVerifica
//Received a verification request for non-pending subscription, return a NotFound response
if (!pendingSubs.ContainsKey(subscriptionId)) { return NotFound(); }

Subscription sub = pendingSubs[subscriptionId];
Model.Subscription sub = pendingSubs[subscriptionId];

//Validate verification subcription with our subscription. If a match return challenge
//otherwise return NotFound response.
Expand Down Expand Up @@ -135,7 +339,7 @@ public async Task<IActionResult> Subscribe(string subscriptionUrl, string topic,
var secret = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
var httpClient = new HttpClient();
string subUID = Guid.NewGuid().ToString("n");
var data = new Subscription()
var data = new Model.Subscription()
{
UID = subUID,
Callback = new Uri(this.Request.Scheme + "://" + this.Request.Host + "/client/" + subUID),
Expand Down Expand Up @@ -168,15 +372,15 @@ public async Task<IActionResult> Subscribe(string subscriptionUrl, string topic,
var result = await httpClient.PostAsync(subscriptionUrl, httpcontent);

if (internalModel == null) { internalModel = new ClientModel(); }
return View("WebSubClient", internalModel);
return View(VIEW_NAME, internalModel);
}

[Route("unsubscribe/{subscriptionId}")]
[HttpPost]
public async Task<IActionResult> Unsubscribe(string subscriptionId) {
this.logger.LogDebug($"Unsubscribing subscription {subscriptionId}");
if (!activeSubs.ContainsKey(subscriptionId)) { return View("WebSubClient", internalModel); }
Subscription sub = activeSubs[subscriptionId];
if (!activeSubs.ContainsKey(subscriptionId)) { return View(VIEW_NAME, internalModel); }
Model.Subscription sub = activeSubs[subscriptionId];
sub.Mode = SubscriptionMode.unsubscribe;

var httpClient = new HttpClient();
Expand All @@ -194,7 +398,7 @@ public async Task<IActionResult> Unsubscribe(string subscriptionId) {

activeSubs.Remove(subscriptionId);

return View("WebSubClient", internalModel);
return View(VIEW_NAME, internalModel);
}
#endregion

Expand Down
Loading