Skip to content

Commit

Permalink
Get induction data from TRS
Browse files Browse the repository at this point in the history
  • Loading branch information
gunndabad committed Feb 7, 2025
1 parent 7da6bf3 commit 765b4c1
Show file tree
Hide file tree
Showing 72 changed files with 15,445 additions and 10,656 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public async Task<GetTeacherResponse> Handle(GetTeacherRequest request, Cancella
return null;
}

var person = await _dbContext.Persons.SingleAsync(p => p.PersonId == teacher.Id);

Check failure on line 47 in TeachingRecordSystem/src/TeachingRecordSystem.Api/V1/Handlers/GetTeacherHandler.cs

View workflow job for this annotation

GitHub Actions / TeachingRecordSystem.Api.Tests test results

TeachingRecordSystem.Api.Tests.V1.Operations.GetTeacherTests ► Given_match_returns_ok

Failed test found in: TeachingRecordSystem/tests/TeachingRecordSystem.Api.Tests/TestResults/_fv-az529-638_2025-02-07_12_01_38.trx Error: System.InvalidOperationException : Sequence contains no elements.
Raw output
System.InvalidOperationException : Sequence contains no elements.
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
   at TeachingRecordSystem.Api.V1.Handlers.GetTeacherHandler.Handle(GetTeacherRequest request, CancellationToken cancellationToken) in /home/runner/work/teaching-record-system/teaching-record-system/TeachingRecordSystem/src/TeachingRecordSystem.Api/V1/Handlers/GetTeacherHandler.cs:line 47
   at TeachingRecordSystem.Api.V1.Controllers.TeachersController.GetTeacherAsync(GetTeacherRequest request) in /home/runner/work/teaching-record-system/teaching-record-system/TeachingRecordSystem/src/TeachingRecordSystem.Api/V1/Controllers/TeachersController.cs:line 32
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ExceptionContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at TeachingRecordSystem.Api.Infrastructure.Middleware.AssignRequestedVersionMiddleware.InvokeAsync(HttpContext context) in /home/runner/work/teaching-record-system/teaching-record-system/TeachingRecordSystem/src/TeachingRecordSystem.Api/Infrastructure/Middleware/AssignRequestedVersionMiddleware.cs:line 20
   at Prometheus.HttpMetrics.HttpRequestDurationMiddleware.Invoke(HttpContext context)
   at Prometheus.HttpMetrics.HttpRequestCountMiddleware.Invoke(HttpContext context)
   at Prometheus.HttpMetrics.HttpInProgressMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.TestHost.HttpContextBuilder.<>c__DisplayClass23_0.<<SendAsync>g__RunRequestAsync|0>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.TestHost.ClientHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Mvc.Testing.Handlers.CookieContainerHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Mvc.Testing.Handlers.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   at TeachingRecordSystem.Api.Tests.V1.Operations.GetTeacherTests.Given_match_returns_ok() in /home/runner/work/teaching-record-system/teaching-record-system/TeachingRecordSystem/tests/TeachingRecordSystem.Api.Tests/V1/Operations/GetTeacherTests.cs:line 90

Check failure on line 47 in TeachingRecordSystem/src/TeachingRecordSystem.Api/V1/Handlers/GetTeacherHandler.cs

View workflow job for this annotation

GitHub Actions / TeachingRecordSystem.Api.Tests test results

TeachingRecordSystem.Api.Tests.V1.Operations.GetTeacherTests ► Given_multiple_matches_returns_match_on_TRN

Failed test found in: TeachingRecordSystem/tests/TeachingRecordSystem.Api.Tests/TestResults/_fv-az529-638_2025-02-07_12_01_38.trx Error: System.InvalidOperationException : Sequence contains no elements.
Raw output
System.InvalidOperationException : Sequence contains no elements.
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
   at TeachingRecordSystem.Api.V1.Handlers.GetTeacherHandler.Handle(GetTeacherRequest request, CancellationToken cancellationToken) in /home/runner/work/teaching-record-system/teaching-record-system/TeachingRecordSystem/src/TeachingRecordSystem.Api/V1/Handlers/GetTeacherHandler.cs:line 47
   at TeachingRecordSystem.Api.V1.Controllers.TeachersController.GetTeacherAsync(GetTeacherRequest request) in /home/runner/work/teaching-record-system/teaching-record-system/TeachingRecordSystem/src/TeachingRecordSystem.Api/V1/Controllers/TeachersController.cs:line 32
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ExceptionContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at TeachingRecordSystem.Api.Infrastructure.Middleware.AssignRequestedVersionMiddleware.InvokeAsync(HttpContext context) in /home/runner/work/teaching-record-system/teaching-record-system/TeachingRecordSystem/src/TeachingRecordSystem.Api/Infrastructure/Middleware/AssignRequestedVersionMiddleware.cs:line 20
   at Prometheus.HttpMetrics.HttpRequestDurationMiddleware.Invoke(HttpContext context)
   at Prometheus.HttpMetrics.HttpRequestCountMiddleware.Invoke(HttpContext context)
   at Prometheus.HttpMetrics.HttpInProgressMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.TestHost.HttpContextBuilder.<>c__DisplayClass23_0.<<SendAsync>g__RunRequestAsync|0>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.TestHost.ClientHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Mvc.Testing.Handlers.CookieContainerHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Mvc.Testing.Handlers.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
   at TeachingRecordSystem.Api.Tests.V1.Operations.GetTeacherTests.Given_multiple_matches_returns_match_on_TRN() in /home/runner/work/teaching-record-system/teaching-record-system/TeachingRecordSystem/tests/TeachingRecordSystem.Api.Tests/V1/Operations/GetTeacherTests.cs:line 135

var qualifications = await _dataverseAdapter.GetQualificationsForTeacherAsync(
teacher.Id,
columnNames: new[]
Expand Down Expand Up @@ -71,11 +73,11 @@ public async Task<GetTeacherResponse> Handle(GetTeacherRequest request, Cancella

var hasActiveAlert = await _dbContext.Alerts.Where(a => a.PersonId == teacher.Id && a.IsOpen).AnyAsync();

var response = MapContactToResponse(teacher, hasActiveAlert);
var response = MapContactToResponse(teacher, hasActiveAlert, person);
return response;
}

internal static GetTeacherResponse MapContactToResponse(Contact teacher, bool hasActiveAlert)
internal static GetTeacherResponse MapContactToResponse(Contact teacher, bool hasActiveAlert, PostgresModels.Person person)
{
return new GetTeacherResponse()
{
Expand All @@ -94,25 +96,18 @@ internal static GetTeacherResponse MapContactToResponse(Contact teacher, bool ha

Induction MapInduction()
{
var induction = teacher.Extract<dfeta_induction>();
var inductionStatus = teacher.FormattedValues.ContainsKey(Contact.Fields.dfeta_InductionStatus) ? teacher.FormattedValues[Contact.Fields.dfeta_InductionStatus] : null;
var dqtStatus = person.InductionStatus.ToDqtInductionStatus(out var statusDescription);

return induction != null ?
return dqtStatus != null ?
new Induction()
{
StartDate = induction.dfeta_StartDate,
CompletionDate = induction.dfeta_CompletionDate,
InductionStatusName = inductionStatus,
State = induction.StateCode.Value,
StateName = induction.FormattedValues[dfeta_induction.Fields.StateCode]
StartDate = person.InductionStartDate.ToDateTime(),
CompletionDate = person.InductionCompletedDate.ToDateTime(),
InductionStatusName = statusDescription,
State = dfeta_inductionState.Active,
StateName = "Active"
} :
!string.IsNullOrEmpty(inductionStatus) ?
new Induction()
{
StartDate = null,
CompletionDate = null,
InductionStatusName = inductionStatus
} : null;
null;
}

QualifiedTeacherStatus MapQualifiedTeacherStatus()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,13 @@ protected async Task<FindPersonsResult> CreateResultAsync(IEnumerable<Contact> m
{
var contactsById = matched.ToDictionary(r => r.Id, r => r);

var getAlertsTask = dbContext.Alerts
.Include(a => a.AlertType)
var getPersonsTask = dbContext.Persons
.Include(p => p.Alerts)
.ThenInclude(a => a.AlertType)
.ThenInclude(at => at.AlertCategory)
.Where(a => contactsById.Keys.Contains(a.PersonId))
.GroupBy(a => a.PersonId)
.ToDictionaryAsync(a => a.Key, a => a.ToArray());
.AsSplitQuery()
.Where(p => contactsById.Keys.Contains(p.PersonId))
.ToDictionaryAsync(p => p.PersonId, p => p);

var getPreviousNamesTask = crmQueryDispatcher.ExecuteQueryAsync(new GetPreviousNamesByContactIdsQuery(contactsById.Keys));

Expand All @@ -85,9 +86,9 @@ protected async Task<FindPersonsResult> CreateResultAsync(IEnumerable<Contact> m
dfeta_qtsregistration.Fields.dfeta_PersonId,
dfeta_qtsregistration.Fields.dfeta_TeacherStatusId)));

await Task.WhenAll(getAlertsTask, getPreviousNamesTask, getQtsRegistrationsTask);
await Task.WhenAll(getPersonsTask, getPreviousNamesTask, getQtsRegistrationsTask);

var alerts = getAlertsTask.Result;
var persons = getPersonsTask.Result;
var previousNames = getPreviousNamesTask.Result;
var qtsRegistrations = getQtsRegistrationsTask.Result;

Expand All @@ -100,15 +101,15 @@ protected async Task<FindPersonsResult> CreateResultAsync(IEnumerable<Contact> m
FirstName = r.ResolveFirstName(),
MiddleName = r.ResolveMiddleName(),
LastName = r.ResolveLastName(),
Sanctions = alerts.GetValueOrDefault(r.Id, [])
Sanctions = persons[r.Id].Alerts
.Where(a => Constants.LegacyExposableSanctionCodes.Contains(a.AlertType.DqtSanctionCode) && a.IsOpen)
.Select(a => new SanctionInfo()
{
Code = a.AlertType.DqtSanctionCode!,
StartDate = a.StartDate
})
.AsReadOnly(),
Alerts = alerts.GetValueOrDefault(r.Id, [])
Alerts = persons[r.Id].Alerts
.Where(a => !a.AlertType.InternalOnly)
.Select(a => new Alert()
{
Expand Down Expand Up @@ -137,12 +138,12 @@ protected async Task<FindPersonsResult> CreateResultAsync(IEnumerable<Contact> m
LastName = name.LastName
})
.AsReadOnly(),
InductionStatus = r.dfeta_InductionStatus.ToInductionStatus(),
DqtInductionStatus = r.dfeta_InductionStatus?.ConvertToDqtInductionStatus() is Dtos.DqtInductionStatus inductionStatus ?
InductionStatus = persons[r.Id].InductionStatus,
DqtInductionStatus = persons[r.Id].InductionStatus.ToDqtInductionStatus(out var statusDescription) is string inductionStatus ?
new DqtInductionStatusInfo()
{
Status = inductionStatus,
StatusDescription = inductionStatus.GetDescription()
Status = Enum.Parse<DqtInductionStatus>(inductionStatus, ignoreCase: true),
StatusDescription = statusDescription!
} :
null,
Qts = await QtsInfo.CreateAsync(qtsRegistrations[r.Id].OrderBy(qr => qr.CreatedOn).FirstOrDefault(s => s.dfeta_QTSDate is not null), referenceDataCache, r.dfeta_qtlsdate),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public record GetPersonResult
public required string? EmailAddress { get; set; }
public required QtsInfo? Qts { get; init; }
public required EytsInfo? Eyts { get; init; }
public required Option<GetPersonResultInduction> Induction { get; init; }
public required Option<InductionInfo> Induction { get; init; }
public required Option<GetPersonResultDqtInduction?> DqtInduction { get; init; }
public required Option<IReadOnlyCollection<GetPersonResultInitialTeacherTraining>> InitialTeacherTraining { get; init; }
public required Option<IReadOnlyCollection<GetPersonResultNpqQualification>> NpqQualifications { get; init; }
Expand All @@ -59,18 +59,12 @@ public record GetPersonResult
public required QtlsStatus QtlsStatus { get; set; }
}

public record GetPersonResultInduction : InductionInfo
{
public required string? CertificateUrl { get; init; }
}

public record GetPersonResultDqtInduction
{
public required DateOnly? StartDate { get; init; }
public required DateOnly? EndDate { get; init; }
public required DqtInductionStatus Status { get; init; }
public required string? StatusDescription { get; init; }
public required string? CertificateUrl { get; init; }
public required IReadOnlyCollection<GetPersonResultDqtInductionPeriod> Periods { get; init; }
}

Expand Down Expand Up @@ -266,8 +260,8 @@ async Task<T> WithTrsDbLockAsync<T>(Func<Task<T>> action)
var contact = contactDetail.Contact;
var personId = contact.Id;

var getInductionTask = command.Include.HasFlag(GetPersonCommandIncludes.Induction)
? crmQueryDispatcher.ExecuteQueryAsync(new GetActiveInductionByContactIdQuery(contact.Id))
var getPersonTask = command.Include.HasFlag(GetPersonCommandIncludes.Induction)
? WithTrsDbLockAsync(() => dbContext.Persons.SingleAsync(p => p.PersonId == personId))
: null;

var getIttTask = command.Include.HasFlag(GetPersonCommandIncludes.InitialTeacherTraining) ?
Expand Down Expand Up @@ -371,20 +365,16 @@ async Task<T> WithTrsDbLockAsync<T>(Func<Task<T>> action)

var qts = qtsRegistrations.OrderByDescending(x => x.CreatedOn).FirstOrDefault(qts => qts.dfeta_QTSDate is not null);
var eyts = qtsRegistrations.OrderByDescending(x => x.CreatedOn).FirstOrDefault(qts => qts.dfeta_EYTSDate is not null);
var allTeacherStatuses = await referenceDataCache.GetTeacherStatusesAsync();
var allEarlyYearsStatuses = await referenceDataCache.GetEytsStatusesAsync();
var eytsStatus = eyts is not null ? allEarlyYearsStatuses.Single(x => x.Id == eyts.dfeta_EarlyYearsStatusId.Id) : null;
var qtsStatus = qts is not null ? allTeacherStatuses.Single(x => x.Id == qts.dfeta_TeacherStatusId.Id) : null;

var allowIdSignInWithProhibitions = command.Include.HasFlag(GetPersonCommandIncludes.AllowIdSignInWithProhibitions) ?
Option.Some(contact.dfeta_AllowIDSignInWithProhibitions == true) :
default;

Option<GetPersonResultDqtInduction?> dqtInduction = default;
Option<GetPersonResultInduction> induction = default;
Option<InductionInfo> induction = default;
if (command.Include.HasFlag(GetPersonCommandIncludes.Induction))
{
var mappedInduction = MapInduction((await getInductionTask!).Induction, (await getInductionTask!).InductionPeriods, contact);
var mappedInduction = MapInduction((await getPersonTask!));
dqtInduction = Option.Some(mappedInduction.DqtInduction);
induction = Option.Some(mappedInduction.Induction);
}
Expand Down Expand Up @@ -479,68 +469,38 @@ private static QtlsStatus MapQtlsStatus(DateTime? qtlsDate, bool? qtlsDateHasBee
};
}

private static (GetPersonResultDqtInduction? DqtInduction, GetPersonResultInduction Induction) MapInduction(
dfeta_induction? induction,
IEnumerable<dfeta_inductionperiod>? inductionPeriods,
Contact contact)
private static (GetPersonResultDqtInduction? DqtInduction, InductionInfo Induction) MapInduction(
PostgresModels.Person person)
{
var status = contact.dfeta_InductionStatus.ToInductionStatus();
var dqtStatus = contact.dfeta_InductionStatus?.ConvertToDqtInductionStatus();

var startDate = status.RequiresStartDate()
? induction?.dfeta_StartDate.ToDateOnlyWithDqtBstFix(isLocalTime: true)
var status = person.InductionStatus;
var dqtStatusName = status.ToDqtInductionStatus(out var dqtStatusDescription);
DqtInductionStatus? dqtStatus = dqtStatusName is not null
? Enum.Parse<DqtInductionStatus>(dqtStatusName, ignoreCase: true)
: null;

var completedDate = status.RequiresCompletedDate()
? induction?.dfeta_CompletionDate.ToDateOnlyWithDqtBstFix(isLocalTime: true)
: null;

var canGenerateCertificate = dqtStatus is DqtInductionStatus.Pass or DqtInductionStatus.PassedInWales
&& completedDate.HasValue;

var certificateUrl = canGenerateCertificate ? "/v3/certificates/induction" : null;
var startDate = person.InductionStartDate;
var completedDate = person.InductionCompletedDate;

var dqtInduction = dqtStatus is not null
? new GetPersonResultDqtInduction()
{
StartDate = startDate,
EndDate = completedDate,
Status = dqtStatus.Value,
StatusDescription = contact.dfeta_InductionStatus?.GetDescription(),
CertificateUrl = certificateUrl,
Periods = (inductionPeriods ?? []).Select(MapInductionPeriod).ToArray()
StatusDescription = dqtStatusDescription,
Periods = []
}
: null;

var inductionInfo = new GetPersonResultInduction()
var inductionInfo = new InductionInfo()
{
Status = status,
StartDate = startDate,
CompletedDate = completedDate,
CertificateUrl = certificateUrl,
};

return (dqtInduction, inductionInfo);
}

private static GetPersonResultDqtInductionPeriod MapInductionPeriod(dfeta_inductionperiod inductionPeriod)
{
var appropriateBody = inductionPeriod.Extract<Account>("appropriatebody", Account.PrimaryIdAttribute);

return new GetPersonResultDqtInductionPeriod()
{
StartDate = inductionPeriod.dfeta_StartDate.ToDateOnlyWithDqtBstFix(isLocalTime: true),
EndDate = inductionPeriod.dfeta_EndDate.ToDateOnlyWithDqtBstFix(isLocalTime: true),
Terms = inductionPeriod.dfeta_Numberofterms,
AppropriateBody = appropriateBody is not null ?
new GetPersonResultInductionPeriodAppropriateBody()
{
Name = appropriateBody.Name
} :
null
};
}

private static IReadOnlyCollection<GetPersonResultInitialTeacherTraining> MapInitialTeacherTraining(
dfeta_initialteachertraining[] itt,
bool userHasAppropriateBodyRole)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,13 @@ public async Task<ApiResult<SetQtlsResult>> HandleAsync(SetQtlsCommand command)
return ApiError.PersonNotFound(command.Trn);
}

var induction = await GetInductionWithAppropriateBodyAsync(contact.Id);
var person = await dbContext.Persons.SingleAsync(p => p.PersonId == contact.Id);

var (canSetQtlsDate, reviewTaskDescription) = CanSetQtlsDate(
hasQTS: contact.dfeta_QTSDate.HasValue,
overallInductionStatus: contact.dfeta_InductionStatus,
inductionStatus: induction?.Induction.dfeta_InductionStatus,
hasInductionWithAB: induction?.HasAppropriateBody ?? false,
existingQtlsdate: contact.dfeta_qtlsdate,
hasQts: contact.dfeta_QTSDate.HasValue,
person.InductionStatus,
person.InductionStatusWithoutExemption,
existingQtlsDate: contact.dfeta_qtlsdate,
incomingQtlsDate: command.QtsDate);

if (!canSetQtlsDate)
Expand All @@ -76,7 +75,7 @@ await crmQueryDispatcher.ExecuteQueryAsync(
var hasActiveAlert = await dbContext.Alerts.Where(a => a.PersonId == contact.Id && a.IsOpen).AnyAsync();

await crmQueryDispatcher.ExecuteQueryAsync(
new SetQtlsDateQuery(contact.Id, command.QtsDate, hasActiveAlert, clock.UtcNow))!;
new SetQtlsDateQuery(contact.Id, command.QtsDate, hasActiveAlert, clock.UtcNow));

return SetQtlsResult.Success(new QtlsResult()
{
Expand All @@ -85,33 +84,22 @@ await crmQueryDispatcher.ExecuteQueryAsync(
});
}

private async Task<(dfeta_induction Induction, bool HasAppropriateBody)?> GetInductionWithAppropriateBodyAsync(Guid contactId)
{
var induction = await crmQueryDispatcher.ExecuteQueryAsync(new GetActiveInductionByContactIdQuery(contactId));
if (induction.Induction == null)
{
return null;
}
else
{
var inductionPeriod = induction.InductionPeriods.Where(x => x.dfeta_EndDate == null).OrderByDescending(x => x.dfeta_StartDate).FirstOrDefault();
return (induction.Induction, inductionPeriod != null);
}
}

private (bool CanSetQtlsDate, string? TaskMessage) CanSetQtlsDate(bool hasQTS, dfeta_InductionStatus? overallInductionStatus, dfeta_InductionStatus? inductionStatus, bool hasInductionWithAB, DateTime? existingQtlsdate, DateOnly? incomingQtlsDate) =>
hasQTS switch
private static (bool CanSetQtlsDate, string? TaskMessage) CanSetQtlsDate(
bool hasQts,
InductionStatus currentInductionStatus,
InductionStatus inductionStatusWithoutExemption,
DateTime? existingQtlsDate,
DateOnly? incomingQtlsDate) =>
hasQts switch
{
true when overallInductionStatus == dfeta_InductionStatus.InProgress && !existingQtlsdate.HasValue && incomingQtlsDate.HasValue => (false, $"Unable to set QTLSDate {incomingQtlsDate}, teacher induction currently set to 'In Progress'"),
true when overallInductionStatus == dfeta_InductionStatus.InductionExtended && !hasInductionWithAB && !existingQtlsdate.HasValue && incomingQtlsDate.HasValue => (true, null),
true when overallInductionStatus == dfeta_InductionStatus.InductionExtended && hasInductionWithAB && !existingQtlsdate.HasValue && incomingQtlsDate.HasValue => (false, $"Unable to set QTLSDate {incomingQtlsDate}, teacher induction currently set to 'Induction Extended' claimed with an AB"),
true when overallInductionStatus == dfeta_InductionStatus.Fail => (false, $"Unable to set QTLSDate {incomingQtlsDate}, teacher induction currently set to 'Fail'"),
true when overallInductionStatus == dfeta_InductionStatus.Exempt && inductionStatus == dfeta_InductionStatus.FailedinWales && existingQtlsdate.HasValue && !incomingQtlsDate.HasValue => (false, $"Unable to remove QTLSDate, teacher induction currently set to 'Failed in Wales'"),
true when overallInductionStatus == dfeta_InductionStatus.FailedinWales && !existingQtlsdate.HasValue && incomingQtlsDate.HasValue => (false, $"Unable to set QTLSDate {incomingQtlsDate}, teacher induction currently set to 'Failed in Wales'"),
true when overallInductionStatus == dfeta_InductionStatus.FailedinWales && existingQtlsdate.HasValue && incomingQtlsDate.HasValue => (false, $"Unable to set QTLSDate {incomingQtlsDate}, teacher induction currently set to 'Failed in Wales'"),
true when overallInductionStatus == dfeta_InductionStatus.Exempt && existingQtlsdate.HasValue && !incomingQtlsDate.HasValue => (true, null),
true when currentInductionStatus == InductionStatus.InProgress && !existingQtlsDate.HasValue && incomingQtlsDate.HasValue => (false, $"Unable to set QTLSDate {incomingQtlsDate}, teacher induction currently set to 'In Progress'"),
true when currentInductionStatus == InductionStatus.Failed => (false, $"Unable to set QTLSDate {incomingQtlsDate}, teacher induction currently set to 'Fail'"),
true when currentInductionStatus == InductionStatus.Exempt && inductionStatusWithoutExemption == InductionStatus.FailedInWales && existingQtlsDate.HasValue && !incomingQtlsDate.HasValue => (false, $"Unable to remove QTLSDate, teacher induction currently set to 'Failed in Wales'"),
true when currentInductionStatus == InductionStatus.FailedInWales && !existingQtlsDate.HasValue && incomingQtlsDate.HasValue => (false, $"Unable to set QTLSDate {incomingQtlsDate}, teacher induction currently set to 'Failed in Wales'"),
true when currentInductionStatus == InductionStatus.FailedInWales && existingQtlsDate.HasValue && incomingQtlsDate.HasValue => (false, $"Unable to set QTLSDate {incomingQtlsDate}, teacher induction currently set to 'Failed in Wales'"),
true when currentInductionStatus == InductionStatus.Exempt && existingQtlsDate.HasValue && !incomingQtlsDate.HasValue => (true, null),
false => (true, null),
_ when existingQtlsdate.HasValue && incomingQtlsDate.HasValue && incomingQtlsDate.Value == existingQtlsdate.ToDateOnlyWithDqtBstFix(isLocalTime: false) => (false, $"Unable to set QTLSDate {incomingQtlsDate}, this matches existing QTLS date on teacher record"),
_ when existingQtlsDate.HasValue && incomingQtlsDate.HasValue && incomingQtlsDate.Value == existingQtlsDate.ToDateOnlyWithDqtBstFix(isLocalTime: false) => (false, $"Unable to set QTLSDate {incomingQtlsDate}, this matches existing QTLS date on teacher record"),
_ => (true, null)
};
}
Expand Down
Loading

0 comments on commit 765b4c1

Please sign in to comment.