Skip to content

Commit

Permalink
Job to backfill CAS1 bookings created in delius
Browse files Browse the repository at this point in the history
This commit adds a seed job to backfill active CAS1 bookings created in delius that do not exist in CAS1.

An active booking is one that:

* does not have a departure recorded (and optionally, no arrival recorded)
* does not have a non arrival recorded

We also exclude any bookings where the departure date is before 1/1/2025 or after 1/1/2035. This filters out several bookings where the dates have been set incorrectly but are clearly inactive/complete, most likely because they were created in older versions of delius that did not capture this data.

Note that the [Cas1DeliusBookingImportEntity] table only includes accepted bookings (i.e. not rejected), so these are already filtered out.
  • Loading branch information
davidatkinsuk committed Jan 9, 2025
1 parent 1adbd53 commit 187ca51
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.Table
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.stereotype.Repository
import java.time.LocalDate
import java.time.OffsetDateTime
Expand All @@ -12,7 +13,40 @@ import java.util.UUID
@Repository
interface Cas1DeliusBookingImportRepository : JpaRepository<Cas1DeliusBookingImportEntity, UUID> {
fun findByBookingId(id: UUID): Cas1DeliusBookingImportEntity?
fun findByBookingIdIsNullAndPremisesQcode(qCode: String): List<Cas1DeliusBookingImportEntity>

/**
* Returns all active bookings created in delius that were not created in CAS1
*
* An active booking is one that:
*
* 1. does not have a departure recorded (and optionally, no arrival recorded)
* 2. does not have a non arrival recorded
*
* We also exclude any bookings where the departure date is before 1/1/2025 or after 1/1/2035.
* This filters out several bookings where the dates have been set incorrectly but are clearly inactive/complete,
* most likely because they were created in older versions of delius that did not capture this data
*
* Note that the [Cas1DeliusBookingImportEntity] table only includes accepted bookings (i.e. not rejected),
* so these are already filtered out
*/
@Query(
"""
FROM Cas1DeliusBookingImportEntity i
WHERE
i.bookingId IS NULL AND
i.premisesQcode = :qCode AND
i.departureDate IS NULL AND
i.nonArrivalReasonCode IS NULL AND
i.expectedDepartureDate > :minExpectedDepartureDate AND
i.expectedDepartureDate < :maxExpectedDepartureDate
""",
)
fun findActiveBookingsCreatedInDelius(
qCode: String,
minExpectedDepartureDate: LocalDate,
maxExpectedDepartureDate: LocalDate,
): List<Cas1DeliusBookingImportEntity>
}

@Entity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import uk.gov.justice.digital.hmpps.approvedpremisesapi.config.SeedConfig
import uk.gov.justice.digital.hmpps.approvedpremisesapi.seed.cas1.ApStaffUsersSeedJob
import uk.gov.justice.digital.hmpps.approvedpremisesapi.seed.cas1.ApprovedPremisesBookingCancelSeedJob
import uk.gov.justice.digital.hmpps.approvedpremisesapi.seed.cas1.ApprovedPremisesRoomsSeedJob
import uk.gov.justice.digital.hmpps.approvedpremisesapi.seed.cas1.Cas1BackfillSpaceBookingsCreatedInDelius
import uk.gov.justice.digital.hmpps.approvedpremisesapi.seed.cas1.Cas1BackfillActiveSpaceBookingsCreatedInDelius
import uk.gov.justice.digital.hmpps.approvedpremisesapi.seed.cas1.Cas1BookingToSpaceBookingSeedJob
import uk.gov.justice.digital.hmpps.approvedpremisesapi.seed.cas1.Cas1CruManagementAreaSeedJob
import uk.gov.justice.digital.hmpps.approvedpremisesapi.seed.cas1.Cas1DomainEventReplaySeedJob
Expand Down Expand Up @@ -91,7 +91,7 @@ class SeedService(
SeedFileType.approvedPremisesImportDeliusReferrals -> getBean(Cas1ImportDeliusReferralsSeedJob::class)
SeedFileType.approvedPremisesUpdateSpaceBooking -> getBean(Cas1UpdateSpaceBookingSeedJob::class)
SeedFileType.temporaryAccommodationReferralRejection -> getBean(Cas3ReferralRejectionSeedJob::class)
SeedFileType.approvedPremisesBackfillSpaceBookingsCreatedInDelius -> getBean(Cas1BackfillSpaceBookingsCreatedInDelius::class)
SeedFileType.approvedPremisesBackfillActiveSpaceBookingsCreatedInDelius -> getBean(Cas1BackfillActiveSpaceBookingsCreatedInDelius::class)
}

val seedStarted = LocalDateTime.now()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import uk.gov.justice.digital.hmpps.approvedpremisesapi.model.PersonSummaryInfoR
import uk.gov.justice.digital.hmpps.approvedpremisesapi.seed.SeedJob
import uk.gov.justice.digital.hmpps.approvedpremisesapi.service.EnvironmentService
import uk.gov.justice.digital.hmpps.approvedpremisesapi.service.OffenderService
import java.time.LocalDate
import java.time.OffsetDateTime
import java.util.UUID

Expand All @@ -25,7 +26,7 @@ import java.util.UUID
* 'cas1_delius_booking_import' table using the [Cas1ImportDeliusReferralsSeedJob] job
*/
@Service
class Cas1BackfillSpaceBookingsCreatedInDelius(
class Cas1BackfillActiveSpaceBookingsCreatedInDelius(
private val approvedPremisesRepository: ApprovedPremisesRepository,
private val cas1DeliusBookingImportRepository: Cas1DeliusBookingImportRepository,
private val cas1BookingManagementInfoService: Cas1BookingManagementInfoService,
Expand Down Expand Up @@ -58,25 +59,27 @@ class Cas1BackfillSpaceBookingsCreatedInDelius(
}
}

@SuppressWarnings("TooGenericExceptionCaught")
@SuppressWarnings("TooGenericExceptionCaught", "MagicNumber")
private fun migratePremise(qCode: String) {
val premises = approvedPremisesRepository.findByQCode(qCode) ?: error("Premises with qcode $qCode not found")

if (!premises.supportsSpaceBookings) {
error("premise ${premises.name} doesn't support space bookings, can't migrate bookings")
}

val referrals = cas1DeliusBookingImportRepository.findByBookingIdIsNullAndPremisesQcode(qCode)
val referrals = cas1DeliusBookingImportRepository.findActiveBookingsCreatedInDelius(
qCode = qCode,
minExpectedDepartureDate = LocalDate.of(2025, 1, 1),
maxExpectedDepartureDate = LocalDate.of(2035, 1, 1),
)

log.info("Will create ${referrals.size} space bookings for premise ${premises.name} ($qCode)")

if (referrals.isEmpty()) {
return
}

// TODO: we need a version of this that will segment calls to the backend
// can use for CAS3 report that does that?
val crnToName = offenderService.getPersonSummaryInfoResults(
val crnToName = offenderService.getPersonSummaryInfoResultsInBatches(
crns = referrals.map { it.crn }.toSet(),
limitedAccessStrategy = OffenderService.LimitedAccessStrategy.IgnoreLimitedAccess,
).associate { personSummaryInfoResult ->
Expand Down Expand Up @@ -106,6 +109,8 @@ class Cas1BackfillSpaceBookingsCreatedInDelius(
crnToName = crnToName,
)
}

log.info("Have crated ${referrals.size} space bookings for premise ${premises.name} ($qCode)")
}

private fun createSpaceBooking(
Expand Down Expand Up @@ -146,10 +151,10 @@ class Cas1BackfillSpaceBookingsCreatedInDelius(
expectedDepartureDate = deliusReferral.expectedDepartureDate!!,
actualArrivalDate = managementInfo.arrivedAtDate,
actualArrivalTime = managementInfo.arrivedAtTime,
actualDepartureDate = managementInfo.departedAtDate,
actualDepartureTime = managementInfo.departedAtTime,
actualDepartureDate = null,
actualDepartureTime = null,
canonicalArrivalDate = managementInfo.arrivedAtDate ?: deliusReferral.expectedArrivalDate,
canonicalDepartureDate = managementInfo.departedAtDate ?: deliusReferral.expectedDepartureDate!!,
canonicalDepartureDate = deliusReferral.expectedDepartureDate!!,
crn = deliusReferral.crn,
keyWorkerStaffCode = managementInfo.keyWorkerStaffCode,
keyWorkerName = managementInfo.keyWorkerName,
Expand All @@ -158,13 +163,13 @@ class Cas1BackfillSpaceBookingsCreatedInDelius(
cancellationRecordedAt = null,
cancellationReason = null,
cancellationReasonNotes = null,
departureMoveOnCategory = managementInfo.departureMoveOnCategory,
departureReason = managementInfo.departureReason,
departureNotes = managementInfo.departureNotes,
departureMoveOnCategory = null,
departureReason = null,
departureNotes = null,
criteria = emptyList<CharacteristicEntity>().toMutableList(),
nonArrivalReason = managementInfo.nonArrivalReason,
nonArrivalConfirmedAt = managementInfo.nonArrivalConfirmedAt?.toInstant(),
nonArrivalNotes = managementInfo.nonArrivalNotes,
nonArrivalReason = null,
nonArrivalConfirmedAt = null,
nonArrivalNotes = null,
deliusEventNumber = deliusReferral.eventNumber,
migratedManagementInfoFrom = managementInfo.source,
),
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/static/_shared.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3726,7 +3726,7 @@ components:
- approved_premises_space_planning_dry_run
- approved_premises_import_delius_referrals
- approved_premises_update_space_booking
- approved_premises_backfill_space_bookings_created_in_delius
- approved_premises_backfill_active_space_bookings_created_in_delius
- temporary_accommodation_referral_rejection
SeedFromExcelFileType:
type: string
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/static/codegen/built-api-spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8027,7 +8027,7 @@ components:
- approved_premises_space_planning_dry_run
- approved_premises_import_delius_referrals
- approved_premises_update_space_booking
- approved_premises_backfill_space_bookings_created_in_delius
- approved_premises_backfill_active_space_bookings_created_in_delius
- temporary_accommodation_referral_rejection
SeedFromExcelFileType:
type: string
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/static/codegen/built-cas1-api-spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4948,7 +4948,7 @@ components:
- approved_premises_space_planning_dry_run
- approved_premises_import_delius_referrals
- approved_premises_update_space_booking
- approved_premises_backfill_space_bookings_created_in_delius
- approved_premises_backfill_active_space_bookings_created_in_delius
- temporary_accommodation_referral_rejection
SeedFromExcelFileType:
type: string
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/static/codegen/built-cas2-api-spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4317,7 +4317,7 @@ components:
- approved_premises_space_planning_dry_run
- approved_premises_import_delius_referrals
- approved_premises_update_space_booking
- approved_premises_backfill_space_bookings_created_in_delius
- approved_premises_backfill_active_space_bookings_created_in_delius
- temporary_accommodation_referral_rejection
SeedFromExcelFileType:
type: string
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/static/codegen/built-cas3-api-spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3825,7 +3825,7 @@ components:
- approved_premises_space_planning_dry_run
- approved_premises_import_delius_referrals
- approved_premises_update_space_booking
- approved_premises_backfill_space_bookings_created_in_delius
- approved_premises_backfill_active_space_bookings_created_in_delius
- temporary_accommodation_referral_rejection
SeedFromExcelFileType:
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,13 @@ import uk.gov.justice.digital.hmpps.approvedpremisesapi.repository.Cas1SpaceBook
import uk.gov.justice.digital.hmpps.approvedpremisesapi.seed.CsvBuilder
import uk.gov.justice.digital.hmpps.approvedpremisesapi.seed.cas1.Cas1CreateMissingReferralsSeedCsvRow
import uk.gov.justice.digital.hmpps.approvedpremisesapi.util.isWithinTheLastMinute
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.time.ZoneOffset
import java.util.UUID

class SeedCas1BackfillSpaceBookingsCreatedInDeliusTest : SeedTestBase() {
class SeedCas1BackfillActiveSpaceBookingsCreatedInDeliusTest : SeedTestBase() {

@Autowired
lateinit var cas1SpaceBookingTestRepository: Cas1SpaceBookingTestRepository
Expand Down Expand Up @@ -107,11 +106,11 @@ class SeedCas1BackfillSpaceBookingsCreatedInDeliusTest : SeedTestBase() {
moveOnCategoryDescription = null,
expectedArrivalDate = LocalDate.of(2024, 5, 9),
arrivalDate = LocalDate.of(2024, 5, 2),
expectedDepartureDate = LocalDate.of(2024, 6, 2),
departureDate = LocalDate.of(2024, 5, 4),
expectedDepartureDate = LocalDate.of(2025, 6, 2),
departureDate = null,
nonArrivalDate = LocalDate.of(3000, 1, 1),
nonArrivalContactDatetime = OffsetDateTime.of(LocalDateTime.of(2024, 2, 1, 9, 58, 23), ZoneOffset.UTC),
nonArrivalReasonCode = "narc1",
nonArrivalReasonCode = null,
nonArrivalReasonDescription = null,
nonArrivalNotes = "the non arrival notes",
premisesQcode = premises.qCode,
Expand Down Expand Up @@ -139,12 +138,44 @@ class SeedCas1BackfillSpaceBookingsCreatedInDeliusTest : SeedTestBase() {
),
)

// booking with non arrival recorded is ignored
deliusBookingImportRepository.save(
deliusBooking.copy(
id = UUID.randomUUID(),
nonArrivalReasonCode = "narc1",
),
)

// booking with departure recorded is ignore
deliusBookingImportRepository.save(
deliusBooking.copy(
id = UUID.randomUUID(),
departureDate = LocalDate.of(2025, 5, 4),
),
)

// booking with departure before 2025-1-1 is ignored
deliusBookingImportRepository.save(
deliusBooking.copy(
id = UUID.randomUUID(),
departureDate = LocalDate.of(2025, 1, 1).minusDays(1),
),
)

// booking with departure after 2035-1-1 is ignored
deliusBookingImportRepository.save(
deliusBooking.copy(
id = UUID.randomUUID(),
departureDate = LocalDate.of(2035, 1, 1).plusDays(1),
),
)

withCsv(
"valid-csv",
rowsToCsv(listOf(Cas1CreateMissingReferralsSeedCsvRow(premises.qCode))),
)

seedService.seedData(SeedFileType.approvedPremisesBackfillSpaceBookingsCreatedInDelius, "valid-csv.csv")
seedService.seedData(SeedFileType.approvedPremisesBackfillActiveSpaceBookingsCreatedInDelius, "valid-csv.csv")

val premiseSpaceBookings = cas1SpaceBookingTestRepository.findByPremisesId(premises.id)
assertThat(premiseSpaceBookings).hasSize(1)
Expand All @@ -157,13 +188,13 @@ class SeedCas1BackfillSpaceBookingsCreatedInDeliusTest : SeedTestBase() {
assertThat(migratedBooking1.placementRequest).isNull()
assertThat(migratedBooking1.createdBy).isNull()
assertThat(migratedBooking1.expectedArrivalDate).isEqualTo(LocalDate.of(2024, 5, 9))
assertThat(migratedBooking1.expectedDepartureDate).isEqualTo(LocalDate.of(2024, 6, 2))
assertThat(migratedBooking1.expectedDepartureDate).isEqualTo(LocalDate.of(2025, 6, 2))
assertThat(migratedBooking1.actualArrivalDate).isEqualTo(LocalDate.parse("2024-05-02"))
assertThat(migratedBooking1.actualArrivalTime).isNull()
assertThat(migratedBooking1.actualDepartureDate).isEqualTo(LocalDate.parse("2024-05-04"))
assertThat(migratedBooking1.actualDepartureDate).isNull()
assertThat(migratedBooking1.actualDepartureTime).isNull()
assertThat(migratedBooking1.canonicalArrivalDate).isEqualTo(LocalDate.of(2024, 5, 2))
assertThat(migratedBooking1.canonicalDepartureDate).isEqualTo(LocalDate.of(2024, 5, 4))
assertThat(migratedBooking1.canonicalDepartureDate).isEqualTo(LocalDate.of(2025, 6, 2))
assertThat(migratedBooking1.keyWorkerName).isEqualTo("kay werker")
assertThat(migratedBooking1.keyWorkerStaffCode).isEqualTo("kw001")
assertThat(migratedBooking1.keyWorkerAssignedAt).isNull()
Expand All @@ -172,12 +203,13 @@ class SeedCas1BackfillSpaceBookingsCreatedInDeliusTest : SeedTestBase() {
assertThat(migratedBooking1.cancellationOccurredAt).isNull()
assertThat(migratedBooking1.cancellationRecordedAt).isNull()
assertThat(migratedBooking1.cancellationReasonNotes).isNull()
assertThat(migratedBooking1.departureReason).isEqualTo(departureReasonActive)
assertThat(migratedBooking1.departureMoveOnCategory).isEqualTo(moveOnCategory)
assertThat(migratedBooking1.departureReason).isNull()
assertThat(migratedBooking1.departureMoveOnCategory).isNull()
assertThat(migratedBooking1.departureNotes).isNull()
assertThat(migratedBooking1.criteria).isEmpty()
assertThat(migratedBooking1.nonArrivalReason).isEqualTo(nonArrivalReasonCode)
assertThat(migratedBooking1.nonArrivalConfirmedAt).isEqualTo(Instant.parse("2024-02-01T09:58:23.00Z"))
assertThat(migratedBooking1.nonArrivalNotes).isEqualTo("the non arrival notes")
assertThat(migratedBooking1.nonArrivalReason).isNull()
assertThat(migratedBooking1.nonArrivalConfirmedAt).isNull()
assertThat(migratedBooking1.nonArrivalNotes).isNull()
assertThat(migratedBooking1.migratedManagementInfoFrom).isEqualTo(ManagementInfoSource.DELIUS)

val offlineApplication1 = migratedBooking1.offlineApplication!!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.ApprovedPremi
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.Cas1SpaceBookingRepository
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.OfflineApplicationRepository
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.cas1.Cas1DeliusBookingImportRepository
import uk.gov.justice.digital.hmpps.approvedpremisesapi.seed.cas1.Cas1BackfillSpaceBookingsCreatedInDelius
import uk.gov.justice.digital.hmpps.approvedpremisesapi.seed.cas1.Cas1BackfillActiveSpaceBookingsCreatedInDelius
import uk.gov.justice.digital.hmpps.approvedpremisesapi.seed.cas1.Cas1BookingManagementInfoService
import uk.gov.justice.digital.hmpps.approvedpremisesapi.seed.cas1.Cas1CreateMissingReferralsSeedCsvRow
import uk.gov.justice.digital.hmpps.approvedpremisesapi.service.EnvironmentService
import uk.gov.justice.digital.hmpps.approvedpremisesapi.service.OffenderService

class Cas1BackfillSpaceBookingsCreatedInDeliusTest {
class Cas1BackfillActiveSpaceBookingsCreatedInDeliusTest {

private val approvedPremisesRepository = mockk<ApprovedPremisesRepository>()

private val seedJob = Cas1BackfillSpaceBookingsCreatedInDelius(
private val seedJob = Cas1BackfillActiveSpaceBookingsCreatedInDelius(
approvedPremisesRepository = approvedPremisesRepository,
cas1DeliusBookingImportRepository = mockk<Cas1DeliusBookingImportRepository>(),
cas1BookingManagementInfoService = mockk<Cas1BookingManagementInfoService>(),
Expand Down

0 comments on commit 187ca51

Please sign in to comment.