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

feat: participant management data service migration #643

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion application/CohortManager/compose.cohort-distribution.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ services:
environment:
- ASPNETCORE_URLS=http://*:7083
- ExceptionFunctionURL=http://localhost:7070/api/CreateException
- DtOsDatabaseConnectionString=Server=localhost,1433;Database=${DB_NAME};User Id=SA;Password=${PASSWORD};TrustServerCertificate=True
- ParticipantManagementUrl=http://localhost:7994/api/ParticipantManagementDataService
- DemographicDataFunctionURL=http://localhost:7076/api/DemographicDataFunction

validate-cohort-distribution-record:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,27 @@ namespace NHS.CohortManager.CohortDistributionService;
using Model;
using Model.Enums;
using Data.Database;
using DataServices.Client;

public class CreateCohortDistribution
{
private readonly ILogger<CreateCohortDistribution> _logger;
private readonly ICallFunction _callFunction;
private readonly ICohortDistributionHelper _CohortDistributionHelper;
private readonly IExceptionHandler _exceptionHandler;
private readonly IParticipantManagerData _participantManagerData;
private readonly IAzureQueueStorageHelper _azureQueueStorageHelper;

public CreateCohortDistribution(ILogger<CreateCohortDistribution> logger,
ICallFunction callFunction,
ICohortDistributionHelper CohortDistributionHelper,
IExceptionHandler exceptionHandler,
IParticipantManagerData participantManagerData,
IDataServiceClient<ParticipantManagement> participantManagementClient,
IAzureQueueStorageHelper azureQueueStorageHelper)
{
_logger = logger;
_callFunction = callFunction;
_CohortDistributionHelper = CohortDistributionHelper;
_exceptionHandler = exceptionHandler;
_participantManagerData = participantManagerData;
_azureQueueStorageHelper = azureQueueStorageHelper;
}

Expand All @@ -39,32 +38,20 @@ public async Task RunAsync([QueueTrigger("%CohortQueueName%", Connection = "Azur
{
if (string.IsNullOrWhiteSpace(basicParticipantCsvRecord.ScreeningService) || string.IsNullOrWhiteSpace(basicParticipantCsvRecord.NhsNumber))
{
string logMessage = $"One or more of the required parameters is missing.";
_logger.LogError(logMessage);
await HandleErrorResponseAsync(logMessage, null, basicParticipantCsvRecord.FileName);
await HandleErrorResponseAsync("One or more of the required parameters is missing.", null, basicParticipantCsvRecord.FileName);
return;
}

try
{
// Retrieve participant data
var participantData = await _CohortDistributionHelper.RetrieveParticipantDataAsync(basicParticipantCsvRecord);
if (participantData == null)
if (participantData == null || string.IsNullOrEmpty(participantData.ScreeningServiceId))
{
_logger.LogInformation("Participant data in cohort distribution was null");
await HandleErrorResponseAsync("There was a problem getting participant data in cohort distribution", participantData, basicParticipantCsvRecord.FileName);
await HandleErrorResponseAsync("Participant data returned from database is missing required fields", participantData, basicParticipantCsvRecord.FileName);
return;
}

if (string.IsNullOrEmpty(participantData.ScreeningServiceId))
{
_logger.LogInformation("Participant data was missing ScreeningServiceId");
await HandleErrorResponseAsync("There was a problem getting participant data in cohort distribution", participantData, basicParticipantCsvRecord.FileName);
return;
}

_logger.LogInformation("Participant data Screening Id: {participantData}", participantData.ScreeningServiceId);

// Allocate service provider
var serviceProvider = EnumHelper.GetDisplayName(ServiceProvider.BSS);
if (!string.IsNullOrEmpty(participantData.Postcode))
Expand All @@ -76,47 +63,47 @@ public async Task RunAsync([QueueTrigger("%CohortQueueName%", Connection = "Azur
return;
}
}

// Check if participant has exceptions
bool ignoreParticipantExceptions = Environment.GetEnvironmentVariable("IgnoreParticipantExceptions") == "true";
bool participantHasException = participantData.ExceptionFlag == 1;

var ignoreParticipantExceptions = (bool)DatabaseHelper.ConvertBoolStringToBoolByType("IgnoreParticipantExceptions", DataTypes.Boolean);

if (ParticipantHasException(basicParticipantCsvRecord.NhsNumber, participantData.ScreeningServiceId) && !ignoreParticipantExceptions) // Will only run if IgnoreParticipantExceptions is false.
if (participantHasException && !ignoreParticipantExceptions) // Will only run if IgnoreParticipantExceptions is false.
{
var ParticipantExceptionErrorMessage = $"Unable to add to cohort distribution. As participant with ParticipantId: {participantData.ParticipantId}. Has an Exception against it";
_logger.LogInformation(ParticipantExceptionErrorMessage, participantData.ParticipantId);
await HandleErrorResponseAsync(ParticipantExceptionErrorMessage, participantData, basicParticipantCsvRecord.FileName);
await HandleErrorResponseAsync($"Unable to add to cohort distribution. As participant with ParticipantId: {participantData.ParticipantId}. Has an Exception against it",
participantData, basicParticipantCsvRecord.FileName);
return;
}
else
{
_logger.LogInformation("Ignore Participant Exceptions is enabled, Record will be processed");
}

// Validate cohort distribution record & transform data service
// Validation
participantData.RecordType = basicParticipantCsvRecord.RecordType;
var validationRecordCreated = await _CohortDistributionHelper.ValidateCohortDistributionRecordAsync(basicParticipantCsvRecord.NhsNumber, basicParticipantCsvRecord.FileName, participantData);
if (!validationRecordCreated || ignoreParticipantExceptions)

if (validationRecordCreated && !ignoreParticipantExceptions)
{
_logger.LogInformation("Validation has passed the record with NHS number: REDACTED will be added to the database");
var transformedParticipant = await _CohortDistributionHelper.TransformParticipantAsync(serviceProvider, participantData);
if (transformedParticipant == null)
{
_logger.LogError("The transform participant returned null in cohort distribution");
await HandleErrorResponseAsync("the transformed participant returned null from the transform participant function", transformedParticipant, basicParticipantCsvRecord.FileName);
return;
}
var errorMessage = $"Validation error: A rule triggered a fatal error, preventing the cohort distribution record with participant Id {participantData.ParticipantId} from being added to the database";
_logger.LogInformation(errorMessage);
await _exceptionHandler.CreateRecordValidationExceptionLog(participantData.NhsNumber, basicParticipantCsvRecord.FileName, errorMessage, serviceProvider, JsonSerializer.Serialize(participantData));
return;
}
_logger.LogInformation("Validation has passed or exceptions are ignored, the record with participant id: {ParticipantId} will be added to the database", participantData.ParticipantId);

var cohortAddResponse = await AddCohortDistribution(transformedParticipant);
if (cohortAddResponse.StatusCode != HttpStatusCode.OK)
{
await HandleErrorResponseAsync("The transformed participant returned null from the transform participant function", transformedParticipant, basicParticipantCsvRecord.FileName);
return;
}
_logger.LogInformation("Participant has been successfully put on the cohort distribution table");
// Transformation
var transformedParticipant = await _CohortDistributionHelper.TransformParticipantAsync(serviceProvider, participantData);
if (transformedParticipant == null)
{
await HandleErrorResponseAsync("The transformed participant returned null from the transform participant function", transformedParticipant, basicParticipantCsvRecord.FileName);
return;
}

// Add to cohort distribution table
var cohortAddResponse = await AddCohortDistribution(transformedParticipant);
if (cohortAddResponse.StatusCode != HttpStatusCode.OK)
{
await HandleErrorResponseAsync("Failed to add the participant to the Cohort Distribution table", transformedParticipant, basicParticipantCsvRecord.FileName);
return;
}
var errorMessage = $"Validation error: A rule triggered a fatal error, preventing the cohort distribution record with participant Id {participantData.ParticipantId} from being added to the database";
_logger.LogInformation(errorMessage);
await _exceptionHandler.CreateRecordValidationExceptionLog(participantData.NhsNumber, basicParticipantCsvRecord.FileName, errorMessage, serviceProvider, JsonSerializer.Serialize(participantData));
_logger.LogInformation("Participant has been successfully put on the cohort distribution table");
}
catch (Exception ex)
{
Expand All @@ -129,6 +116,7 @@ public async Task RunAsync([QueueTrigger("%CohortQueueName%", Connection = "Azur

private async Task HandleErrorResponseAsync(string errorMessage, CohortDistributionParticipant cohortDistributionParticipant, string fileName)
{
_logger.LogError(errorMessage);
var participant = new Participant();
if (cohortDistributionParticipant != null)
{
Expand All @@ -148,11 +136,4 @@ private async Task<HttpWebResponse> AddCohortDistribution(CohortDistributionPart
_logger.LogInformation("Called {AddCohortDistribution} function", nameof(AddCohortDistribution));
return response;
}

private bool ParticipantHasException(string nhsNumber, string screeningId)
{
var participant = _participantManagerData.GetParticipant(nhsNumber, screeningId);
var exceptionFlag = Enum.TryParse(participant.ExceptionFlag, out Exists value) ? value : Exists.No;
return exceptionFlag == Exists.Yes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using DataServices.Client;
using Model;

var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
Expand All @@ -12,7 +14,6 @@
services.AddSingleton<ICreateResponse, CreateResponse>();
services.AddSingleton<ICohortDistributionHelper, CohortDistributionHelper>();
services.TryAddTransient<IDatabaseHelper, DatabaseHelper>();
services.TryAddTransient<IParticipantManagerData, ParticipantManagerData>();
})
.AddAzureQueues()
.AddDatabaseConnection()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@

var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.AddDataServicesHandler()
.AddDataService<ParticipantManagement>(Environment.GetEnvironmentVariable("ParticipantManagementUrl"))
.Build()
.ConfigureServices(services =>
{
services.AddSingleton<ICallFunction, CallFunction>();
services.AddSingleton<ICreateResponse, CreateResponse>();
services.AddTransient<IParticipantManagerData, ParticipantManagerData>();
services.AddSingleton<IDatabaseHelper, DatabaseHelper>();
services.AddSingleton<ICallFunction, CallFunction>();
services.AddSingleton<ICreateParticipant, CreateParticipant>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,28 @@ namespace NHS.CohortManager.CohortDistributionService;
using System.Text;
using System.Text.Json;
using Data.Database;
using DataServices.Client;

public class RetrieveParticipantData
{
private readonly ICreateResponse _createResponse;
private readonly ILogger<RetrieveParticipantData> _logger;
private readonly IParticipantManagerData _participantManagerData;

private readonly ICallFunction _callFunction;
private readonly ICreateParticipant _createParticipant;
private readonly IExceptionHandler _exceptionHandler;
private readonly IDataServiceClient<ParticipantManagement> _participantManagementClient;

public RetrieveParticipantData(ICreateResponse createResponse, ILogger<RetrieveParticipantData> logger, IParticipantManagerData participantManagerData, ICreateParticipant createParticipant, IExceptionHandler exceptionHandler, ICallFunction callFunction)
public RetrieveParticipantData(ICreateResponse createResponse, ILogger<RetrieveParticipantData> logger,
IDataServiceClient<ParticipantManagement> participantManagementClient,
ICreateParticipant createParticipant, IExceptionHandler exceptionHandler,
ICallFunction callFunction)
{
_createResponse = createResponse;
_logger = logger;
_participantManagerData = participantManagerData;
_callFunction = callFunction;
_createParticipant = createParticipant;
_exceptionHandler = exceptionHandler;
_participantManagementClient = participantManagementClient;
}

[Function("RetrieveParticipantData")]
Expand All @@ -48,20 +51,20 @@ public async Task<HttpResponseData> RunAsync([HttpTrigger(AuthorizationLevel.Ano
catch (Exception ex)
{
_logger.LogError(ex, ex.Message);
return _createResponse.CreateHttpResponse(HttpStatusCode.InternalServerError, req);
return _createResponse.CreateHttpResponse(HttpStatusCode.BadRequest, req);
}

try
{
var participantData = await _participantManagementClient.GetSingleByFilter(p => p.NHSNumber == long.Parse(requestBody.NhsNumber) &&
Copy link
Contributor

Choose a reason for hiding this comment

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

I want to double check what SQL is created from this before approving

p.ScreeningId == long.Parse(requestBody.ScreeningService));
_logger.LogInformation("Got the participant. ScreeningId: {ScreeningServiceId}", participantData.ScreeningId);

var demographicFunctionParams = new Dictionary<string, string>()
{
{"Id", requestBody.NhsNumber }
};


var participantData = _participantManagerData.GetParticipantFromIDAndScreeningService(requestBody);
_logger.LogInformation("Got the participant. ScreeningId: {ScreeningServiceId}", participantData.ScreeningId);

var demographicDataJson = await _callFunction.SendGet(Environment.GetEnvironmentVariable("DemographicDataFunctionURL"), demographicFunctionParams);

var demographicData = JsonSerializer.Deserialize<Demographic>(demographicDataJson);
Expand All @@ -82,7 +85,7 @@ public async Task<HttpResponseData> RunAsync([HttpTrigger(AuthorizationLevel.Ano
{
_logger.LogError(ex, "Retrieve participant data failed.\nMessage: {Message}\nStack Trace: {StackTrace}", ex.Message, ex.StackTrace);
await _exceptionHandler.CreateSystemExceptionLogFromNhsNumber(ex, requestBody.NhsNumber, "", "", JsonSerializer.Serialize(participant) ?? "N/A");
return _createResponse.CreateHttpResponse(HttpStatusCode.BadRequest, req);
return _createResponse.CreateHttpResponse(HttpStatusCode.InternalServerError, req);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ public Participant CreateResponseParticipantModel(BasicParticipantData participa
}


public CohortDistributionParticipant CreateCohortDistributionParticipantModel(Participant participant, Demographic demographic)
public CohortDistributionParticipant CreateCohortDistributionParticipantModel(ParticipantManagement participant, Demographic demographic)
{
return new CohortDistributionParticipant
{
ParticipantId = participant.ParticipantId,
NhsNumber = participant.NhsNumber,
ParticipantId = participant.ParticipantId.ToString(),
NhsNumber = participant.NHSNumber.ToString(),
SupersededByNhsNumber = demographic.SupersededByNhsNumber,
PrimaryCareProvider = demographic.PrimaryCareProvider,
PrimaryCareProviderEffectiveFromDate = demographic.PrimaryCareProviderEffectiveFromDate,
Expand Down Expand Up @@ -86,16 +86,15 @@ public CohortDistributionParticipant CreateCohortDistributionParticipantModel(Pa
PreferredLanguage = demographic.PreferredLanguage,
IsInterpreterRequired = demographic.IsInterpreterRequired,
ReasonForRemoval = participant.ReasonForRemoval,
ReasonForRemovalEffectiveFromDate = participant.ReasonForRemovalEffectiveFromDate,
ReasonForRemovalEffectiveFromDate = participant.ReasonForRemovalDate.ToString(),
MWClayson-NHS marked this conversation as resolved.
Show resolved Hide resolved
RecordInsertDateTime = demographic.RecordInsertDateTime,
RecordUpdateDateTime = participant.RecordUpdateDateTime,
ScreeningAcronym = participant.ScreeningAcronym,
ScreeningServiceId = participant.ScreeningId,
ScreeningName = participant.ScreeningName,
RecordUpdateDateTime = participant.RecordUpdateDateTime.ToString(),
ScreeningServiceId = participant.ScreeningId.ToString(),
Extracted = null,
RecordType = participant.RecordType,
CurrentPosting = demographic.CurrentPosting,
CurrentPostingEffectiveFromDate = demographic.CurrentPostingEffectiveFromDate,
ExceptionFlag = participant.ExceptionFlag,
InvalidFlag = demographic.InvalidFlag
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ namespace Common;
public interface ICreateParticipant
{
public Participant CreateResponseParticipantModel(BasicParticipantData participant, Demographic demographic);
public CohortDistributionParticipant CreateCohortDistributionParticipantModel(Participant participant, Demographic demographic);
public CohortDistributionParticipant CreateCohortDistributionParticipantModel(ParticipantManagement participant, Demographic demographic);
}

This file was deleted.

Loading