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

Prevent duplicate degree subject #1467

Merged
merged 3 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
46 changes: 23 additions & 23 deletions GetIntoTeachingApi/Jobs/UpsertCandidateJob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class UpsertCandidateJob : BaseJob
private readonly ICandidateUpserter _upserter;
private readonly INotifyService _notifyService;
private readonly IPerformContextAdapter _contextAdapter;
private readonly IMetricService _metrics;
private readonly IMetricService _metrics;
private readonly IAppSettings _appSettings;
private readonly ILogger<UpsertCandidateJob> _logger;

Expand All @@ -38,23 +38,23 @@ public UpsertCandidateJob(
_appSettings = appSettings;
}

public void Run(string json, PerformContext context)
{
var candidate = json.DeserializeChangeTracked<Candidate>();

if (Deduplicate(Signature(candidate), context, _contextAdapter))
{
_logger.LogInformation("UpsertCandidateJob - Deduplicating");
return;
}

if (_appSettings.IsCrmIntegrationPaused)
{
throw new InvalidOperationException("UpsertCandidateJob - Aborting (CRM integration paused).");
}

_logger.LogInformation("UpsertCandidateJob - Started ({Attempt})", AttemptInfo(context, _contextAdapter));
_logger.LogInformation("UpsertCandidateJob - Payload {Payload}", Redactor.RedactJson(json));
public void Run(string json, PerformContext context)
{
var candidate = json.DeserializeChangeTracked<Candidate>();
if (Deduplicate(Signature(candidate), context, _contextAdapter))
{
_logger.LogInformation("UpsertCandidateJob - Deduplicating");
return;
}
if (_appSettings.IsCrmIntegrationPaused)
{
throw new InvalidOperationException("UpsertCandidateJob - Aborting (CRM integration paused).");
}
_logger.LogInformation("UpsertCandidateJob - Started ({Attempt})", AttemptInfo(context, _contextAdapter));
_logger.LogInformation("UpsertCandidateJob - Payload {Payload}", Redactor.RedactJson(json));

if (IsLastAttempt(context, _contextAdapter))
{
Expand All @@ -68,19 +68,19 @@ public void Run(string json, PerformContext context)
_logger.LogInformation("UpsertCandidateJob - Deleted");
}
else
{
{
_upserter.Upsert(candidate);

_logger.LogInformation("UpsertCandidateJob - Succeeded - {Id}", candidate.Id);
}

var duration = (DateTime.UtcNow - _contextAdapter.GetJobCreatedAt(context)).TotalSeconds;
_metrics.HangfireJobQueueDuration.WithLabels("UpsertCandidateJob").Observe(duration);
_metrics.HangfireJobQueueDuration.WithLabels("UpsertCandidateJob").Observe(duration);
}

private static string Signature(Candidate candidate)
{
return $"{candidate.Id}-{candidate.Email}-{string.Join("", candidate.ChangedPropertyNames)}";
private static string Signature(Candidate candidate)
{
return $"{candidate.Id}-{candidate.Email}-{string.Join("", candidate.ChangedPropertyNames)}";
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Text.Json.Serialization;
using GetIntoTeachingApi.Models.Crm;
using GetIntoTeachingApi.Services;
Expand Down Expand Up @@ -33,6 +34,11 @@ public class SchoolsExperienceSignUp
public string Telephone { get; set; }
public bool? HasDbsCertificate { get; set; }
public DateTime? DbsCertificateIssuedAt { get; set; }
public Guid? QualificationId { get; set; }
public int? DegreeStatusId { get; set; }
public int? DegreeTypeId { get; set; }
public string DegreeSubject { get; set; }
public int? UkDegreeGradeId { get; set; }

[JsonIgnore]
public Candidate Candidate => CreateCandidate();
Expand Down Expand Up @@ -75,6 +81,17 @@ private void PopulateWithCandidate(Candidate candidate)

HasDbsCertificate = candidate.HasDbsCertificate;
DbsCertificateIssuedAt = candidate.DbsCertificateIssuedAt;

var latestQualification = candidate.Qualifications.OrderByDescending(q => q.CreatedAt).FirstOrDefault();

if (latestQualification != null)
{
QualificationId = latestQualification.Id;
DegreeSubject = latestQualification.DegreeSubject;
UkDegreeGradeId = latestQualification.UkDegreeGradeId;
DegreeStatusId = latestQualification.DegreeStatusId;
DegreeTypeId = latestQualification.TypeId;
}
}

private Candidate CreateCandidate()
Expand Down Expand Up @@ -109,6 +126,7 @@ private Candidate CreateCandidate()

ConfigureChannel(candidate);
AcceptPrivacyPolicy(candidate);
AddQualification(candidate);

return candidate;
}
Expand All @@ -132,5 +150,25 @@ private void AcceptPrivacyPolicy(Candidate candidate)
};
}
}

private void AddQualification(Candidate candidate)
{
if (ContainsQualification() && !candidate.Qualifications.Any())
{
candidate.Qualifications.Add(new CandidateQualification()
{
Id = QualificationId,
UkDegreeGradeId = UkDegreeGradeId,
DegreeStatusId = DegreeStatusId,
DegreeSubject = DegreeSubject,
TypeId = DegreeTypeId ?? (int)CandidateQualification.DegreeType.Degree
});
}
}

private bool ContainsQualification()
{
return UkDegreeGradeId != null || DegreeStatusId != null || DegreeSubject != null || DegreeTypeId != null;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are any of these fields mandatory for a Degree to be valid ? Vould missing fiedls here cause problems with the downstream systems ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

nope, they are intentionally optional as older clients will not send the new degree fields. Missing these values means that a qualification record will not be created in the CRM (as is currently the case), but shouldn't break anything downstream.

}
}
}
65 changes: 39 additions & 26 deletions GetIntoTeachingApi/Services/CandidateUpserter.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using GetIntoTeachingApi.Jobs;
using GetIntoTeachingApi.Models.Crm;
using GetIntoTeachingApi.Models.Crm;
using GetIntoTeachingApi.Utils;
using Hangfire;
using static GetIntoTeachingApi.Models.Crm.Candidate;
Expand Down Expand Up @@ -33,8 +33,8 @@ public void Upsert(Candidate candidate)
UpdateEventSubscriptionType(candidate);

SaveCandidate(candidate);

SaveQualifications(qualifications, candidate);

SavePastTeachingPositions(pastTeachingPositions, candidate);
SaveApplicationForms(applicationForms, candidate);
SaveTeachingEventRegistrations(registrations, candidate);
Expand All @@ -43,8 +43,11 @@ public void Upsert(Candidate candidate)
SaveSchoolExperiences(schoolExperiences, candidate);

IncrementCallbackBookingQuotaNumberOfBookings(phoneCall);
}


// Re-add qualifications back to candidate object to ensure it is correctly returned
AddQualifications(qualifications, candidate);
}

private static IEnumerable<TeachingEventRegistration> ClearTeachingEventRegistrations(Candidate candidate)
{
// Due to reasons unknown the event registrations relationship can't be deep-inserted
Expand Down Expand Up @@ -74,15 +77,20 @@ private static IEnumerable<CandidateQualification> ClearQualifications(Candidate
var qualifications = new List<CandidateQualification>(candidate.Qualifications);
candidate.Qualifications.Clear();
return qualifications;
}

private static IEnumerable<CandidateSchoolExperience> ClearSchoolExperiences(Candidate candidate)
{
}

private static void AddQualifications(IEnumerable<CandidateQualification> candidateQualifications, Candidate candidate)
{
candidate.Qualifications.AddRange(candidateQualifications);
}

private static IEnumerable<CandidateSchoolExperience> ClearSchoolExperiences(Candidate candidate)
{
var schoolExperiences = new List<CandidateSchoolExperience>(candidate.SchoolExperiences);
candidate.SchoolExperiences.Clear();
return schoolExperiences;
}
return schoolExperiences;
}

private static PhoneCall ClearPhoneCall(Candidate candidate)
{
if (candidate.PhoneCall == null)
Expand Down Expand Up @@ -140,9 +148,23 @@ private void UpdateEventSubscriptionType(Candidate candidate)
private void SaveCandidate(Candidate candidate)
{
candidate.IsNewRegistrant = candidate.Id == null;

_crm.Save(candidate);
}

private void SaveQualifications(IEnumerable<CandidateQualification> qualifications, Candidate candidate)
{
foreach (var qualification in qualifications)
{
// only add the degree qualification to the CRM if it doesn't already exist
if (!_crm.CandidateHasDegreeQualification((Guid)candidate.Id, CandidateQualification.DegreeType.Degree,
qualification.DegreeSubject))
{
qualification.CandidateId = (Guid)candidate.Id;
// call the CRM immediate so we get qualification ID and prevent duplicate records from being created
_crm.Save(qualification);
}
}
}

private void SaveTeachingEventRegistrations(IEnumerable<TeachingEventRegistration> registrations, Candidate candidate)
{
Expand All @@ -160,16 +182,7 @@ private void SaveTeachingEventRegistrations(IEnumerable<TeachingEventRegistratio
_jobClient.Enqueue<UpsertModelWithCandidateIdJob<TeachingEventRegistration>>((x) => x.Run(json, null));
}
}

private void SaveQualifications(IEnumerable<CandidateQualification> qualifications, Candidate candidate)
{
foreach (var qualification in qualifications)
{
qualification.CandidateId = (Guid)candidate.Id;
string json = qualification.SerializeChangeTracked();
_jobClient.Enqueue<UpsertModelWithCandidateIdJob<CandidateQualification>>((x) => x.Run(json, null));
}
}


private void SaveApplicationForms(IEnumerable<ApplicationForm> applicationForms, Candidate candidate)
{
Expand All @@ -189,8 +202,8 @@ private void SavePastTeachingPositions(IEnumerable<CandidatePastTeachingPosition
string json = pastTeachingPosition.SerializeChangeTracked();
_jobClient.Enqueue<UpsertModelWithCandidateIdJob<CandidatePastTeachingPosition>>((x) => x.Run(json, null));
}
}
}

private void SaveSchoolExperiences(IEnumerable<CandidateSchoolExperience> schoolExperiences, Candidate candidate)
{
foreach (var schoolExperience in schoolExperiences)
Expand Down
Loading
Loading