Skip to content

Commit

Permalink
Implement logic to fetch matching Space Bookings
Browse files Browse the repository at this point in the history
A new query in the SpaceBookingEntity retrieves the matching space bookings for the given premises and day, filtering and ordering as specified.
  • Loading branch information
RobBoothMOJ committed Dec 29, 2024
1 parent c78b064 commit baa0041
Show file tree
Hide file tree
Showing 11 changed files with 856 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.ApprovedPremi
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.UserPermission
import uk.gov.justice.digital.hmpps.approvedpremisesapi.service.UserAccessService
import uk.gov.justice.digital.hmpps.approvedpremisesapi.service.cas1.Cas1PremisesService
import uk.gov.justice.digital.hmpps.approvedpremisesapi.service.cas1.Cas1SpaceBookingDaySummaryService
import uk.gov.justice.digital.hmpps.approvedpremisesapi.transformer.cas1.Cas1PremiseCapacitySummaryTransformer
import uk.gov.justice.digital.hmpps.approvedpremisesapi.transformer.cas1.Cas1PremisesDayTransformer
import uk.gov.justice.digital.hmpps.approvedpremisesapi.transformer.cas1.Cas1PremisesTransformer
Expand All @@ -29,6 +30,7 @@ class Cas1PremisesController(
val cas1PremisesTransformer: Cas1PremisesTransformer,
val cas1PremiseCapacityTransformer: Cas1PremiseCapacitySummaryTransformer,
private val cas1PremisesDayTransformer: Cas1PremisesDayTransformer,
private val cas1SpaceBookingDaySummaryService: Cas1SpaceBookingDaySummaryService,
) : PremisesCas1Delegate {

override fun getPremisesById(premisesId: UUID): ResponseEntity<Cas1PremisesSummary> {
Expand Down Expand Up @@ -97,14 +99,28 @@ class Cas1PremisesController(
userAccessService.ensureCurrentUserHasPermission(UserPermission.CAS1_SPACE_BOOKING_VIEW)

val premiseSummaryInfo = cas1PremisesService.getPremisesSummary(premisesId)
val premiseCapacity = cas1PremisesService.getPremiseCapacity(premisesId, date, date)
val premiseCapacity = cas1PremisesService.getPremiseCapacity(premisesId, date, date, null)
val premisesCapacitySummary = cas1PremiseCapacityTransformer.toCas1PremiseCapacitySummary(
premiseSummaryInfo = extractEntityFromCasResult(premiseSummaryInfo),
premiseCapacity = extractEntityFromCasResult(premiseCapacity),
)

val spaceBookingDaySummaries = extractEntityFromCasResult(
cas1SpaceBookingDaySummaryService.getBookingDaySummaries(
premisesId = premisesId,
date = date,
bookingsCriteriaFilter = bookingsCriteriaFilter,
bookingsSortBy = bookingsSortBy ?: Cas1SpaceBookingDaySummarySortField.PERSON_NAME,
bookingsSortDirection = bookingsSortDirection ?: SortDirection.desc,
),
)

return ResponseEntity.ok().body(
cas1PremisesDayTransformer.toCas1PremisesDaySummary(date, premisesCapacitySummary),
cas1PremisesDayTransformer.toCas1PremisesDaySummary(
date,
premisesCapacitySummary,
spaceBookingDaySummaries,
),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ import jakarta.persistence.Table
import jakarta.persistence.Version
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Sort
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Modifying
import org.springframework.data.jpa.repository.Query
import org.springframework.stereotype.Repository
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.Cas1SpaceBookingEntity.Constants
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.CharacteristicRepository.Constants.CAS1_PROPERTY_NAME_ARSON_SUITABLE
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.CharacteristicRepository.Constants.CAS1_PROPERTY_NAME_ENSUITE
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.CharacteristicRepository.Constants.CAS1_PROPERTY_NAME_SINGLE_ROOM
Expand Down Expand Up @@ -117,6 +119,64 @@ interface Cas1SpaceBookingRepository : JpaRepository<Cas1SpaceBookingEntity, UUI
pageable: Pageable?,
): Page<Cas1SpaceBookingSearchResult>

@Query(
value = """
SELECT
Cast(b.id as varchar),
b.crn as crn,
b.canonical_arrival_date as canonicalArrivalDate,
b.canonical_departure_date as canonicalDepartureDate,
apa.risk_ratings -> 'tier' -> 'value' ->> 'level' as tier,
CASE
WHEN
apa.id IS NOT NULL THEN apa.name
ELSE
oa.name
END as personName,
CASE
WHEN
apa.id IS NOT NULL THEN apa.release_type
ELSE
null
END as releaseType,
(
SELECT STRING_AGG (characteristics.property_name, ',')
FROM cas1_space_bookings_criteria sbc
LEFT OUTER JOIN characteristics ON characteristics.id = sbc.characteristic_id
WHERE sbc.space_booking_id = b.id
GROUP by sbc.space_booking_id
) AS characteristicsPropertyNames
FROM cas1_space_bookings b
LEFT JOIN approved_premises_applications apa
ON
b.approved_premises_application_id = apa.id
LEFT JOIN offline_applications oa
ON
b.offline_application_id = oa.id
WHERE
b.canonical_arrival_date <= :daySummaryDate AND
b.canonical_departure_date >= :daySummaryDate AND
b.premises_id = :premisesId AND
b.cancellation_occurred_at IS NULL AND (
:bookingsCriteriaCount = 0 OR (
SELECT COUNT(*)
FROM
CAS1_SPACE_BOOKINGS_CRITERIA sbc
WHERE b.id = sbc.space_booking_id AND
sbc.characteristic_id IN (:bookingsCriteriaFilter)
) = :bookingsCriteriaCount
)
""",
nativeQuery = true,
)
fun findAllPremisesBookingsForDate(
premisesId: UUID,
daySummaryDate: LocalDate,
bookingsCriteriaFilter: List<UUID>?,
sort: Sort,
bookingsCriteriaCount: Int = bookingsCriteriaFilter?.size ?: 0,
): List<Cas1SpaceBookingDaySummarySearchResult>

@Query(
value = """
SELECT
Expand Down Expand Up @@ -166,6 +226,16 @@ interface Cas1SpaceBookingRepository : JpaRepository<Cas1SpaceBookingEntity, UUI
fun updateEventNumber(applicationId: UUID, eventNumber: String)
}

interface Cas1SpaceBookingDaySummarySearchResult {
val id: UUID
val crn: String
val canonicalArrivalDate: LocalDate
val canonicalDepartureDate: LocalDate
val tier: String
val releaseType: String
val characteristicsPropertyNames: String
}

interface Cas1SpaceBookingSearchResult {
val id: UUID
val crn: String
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package uk.gov.justice.digital.hmpps.approvedpremisesapi.service.cas1

import org.springframework.data.domain.Sort
import org.springframework.stereotype.Service
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.Cas1SpaceBookingCharacteristic
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.Cas1SpaceBookingDaySummary
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.Cas1SpaceBookingDaySummarySortField
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.ServiceName
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.SortDirection
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.Cas1SpaceBookingDaySummarySearchResult
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.Cas1SpaceBookingRepository
import uk.gov.justice.digital.hmpps.approvedpremisesapi.model.PersonSummaryInfoResult
import uk.gov.justice.digital.hmpps.approvedpremisesapi.model.forCrn
import uk.gov.justice.digital.hmpps.approvedpremisesapi.results.CasResult
import uk.gov.justice.digital.hmpps.approvedpremisesapi.service.CharacteristicService
import uk.gov.justice.digital.hmpps.approvedpremisesapi.service.OffenderService
import uk.gov.justice.digital.hmpps.approvedpremisesapi.service.UserAccessService
import uk.gov.justice.digital.hmpps.approvedpremisesapi.service.UserService
import uk.gov.justice.digital.hmpps.approvedpremisesapi.service.cas1LimitedAccessStrategy
import uk.gov.justice.digital.hmpps.approvedpremisesapi.transformer.PersonTransformer
import uk.gov.justice.digital.hmpps.approvedpremisesapi.transformer.cas1.Cas1SpaceBookingDaySummaryTransformer
import java.time.LocalDate
import java.util.UUID

@Service
class Cas1SpaceBookingDaySummaryService(
val userAccessService: UserAccessService,
private val cas1SpaceBookingRepository: Cas1SpaceBookingRepository,
private val characteristicService: CharacteristicService,
private val cas1SpaceBookingDaySummaryTransformer: Cas1SpaceBookingDaySummaryTransformer,
private val offenderService: OffenderService,
private val userService: UserService,
private val cas1PremisesService: Cas1PremisesService,
) {

fun getBookingDaySummaries(
premisesId: UUID,
date: LocalDate,
bookingsCriteriaFilter: List<Cas1SpaceBookingCharacteristic>?,
bookingsSortBy: Cas1SpaceBookingDaySummarySortField,
bookingsSortDirection: SortDirection,
): CasResult<List<Cas1SpaceBookingDaySummary>> {
if (cas1PremisesService.findPremiseById(premisesId) == null) return CasResult.NotFound("premises", premisesId.toString())

val sort = Sort.by(
when (bookingsSortDirection) {
SortDirection.desc -> Sort.Direction.DESC
SortDirection.asc -> Sort.Direction.ASC
},
bookingsSortBy.value,
)

val spaceBookingsForDate = cas1SpaceBookingRepository.findAllPremisesBookingsForDate(
premisesId = premisesId,
daySummaryDate = date,
bookingsCriteriaFilter = getBookingCharacteristicIds(bookingsCriteriaFilter),
sort = sort,
)

val offenderSummaries = getOffenderSummariesForBookings(spaceBookingsForDate)

val spaceBookingDaySummaries =
spaceBookingsForDate.map { bookingSummary ->
cas1SpaceBookingDaySummaryTransformer.toCas1SpaceBookingDaySummary(
bookingSummary,
PersonTransformer()
.personSummaryInfoToPersonSummary(
offenderSummaries.forCrn(bookingSummary.crn),
),
)
}
return CasResult.Success(
spaceBookingDaySummaries,
)
}

private fun getBookingCharacteristicIds(bookingsCriteriaFilter: List<Cas1SpaceBookingCharacteristic>?) =
bookingsCriteriaFilter?.let { bookingCriteria ->
val characteristics = bookingCriteria.map { it.value }
characteristicService.getCharacteristicsByPropertyNames(characteristics, ServiceName.approvedPremises)
.map { characteristic -> characteristic.id }
}

private fun getOffenderSummariesForBookings(spaceBookings: List<Cas1SpaceBookingDaySummarySearchResult>): List<PersonSummaryInfoResult> {
val user = userService.getUserForRequest()
return offenderService.getPersonSummaryInfoResults(
crns = spaceBookings.map { it.crn }.toSet(),
limitedAccessStrategy = user.cas1LimitedAccessStrategy(),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package uk.gov.justice.digital.hmpps.approvedpremisesapi.transformer.cas1
import org.springframework.stereotype.Component
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.Cas1PremiseCapacity
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.Cas1PremisesDaySummary
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.Cas1SpaceBookingDaySummary
import java.time.LocalDate

@Component
Expand All @@ -11,13 +12,14 @@ class Cas1PremisesDayTransformer {
fun toCas1PremisesDaySummary(
date: LocalDate,
premisesCapacity: Cas1PremiseCapacity,
spaceBookings: List<Cas1SpaceBookingDaySummary>,
) =
Cas1PremisesDaySummary(
forDate = date,
previousDate = date.minusDays(1),
nextDate = date.plusDays(1),
capacity = premisesCapacity.capacity.first(),
spaceBookings = emptyList(),
spaceBookings = spaceBookings,
outOfServiceBeds = emptyList(),
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package uk.gov.justice.digital.hmpps.approvedpremisesapi.transformer.cas1

import org.springframework.stereotype.Component
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.Cas1SpaceBookingCharacteristic
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.Cas1SpaceBookingDaySummary
import uk.gov.justice.digital.hmpps.approvedpremisesapi.api.model.PersonSummary
import uk.gov.justice.digital.hmpps.approvedpremisesapi.jpa.entity.Cas1SpaceBookingDaySummarySearchResult

@Component
class Cas1SpaceBookingDaySummaryTransformer {

fun toCas1SpaceBookingDaySummary(
jpa: Cas1SpaceBookingDaySummarySearchResult,
personSummary: PersonSummary,
) = Cas1SpaceBookingDaySummary(
id = jpa.id,
canonicalArrivalDate = jpa.canonicalArrivalDate,
canonicalDepartureDate = jpa.canonicalDepartureDate,
tier = jpa.tier,
releaseType = jpa.releaseType,
essentialCharacteristics = characteristicEntityToCharacteristic(jpa.characteristicsPropertyNames),
person = personSummary,
)

private fun characteristicEntityToCharacteristic(characteristics: String?): List<Cas1SpaceBookingCharacteristic> =
characteristics?.let { characteristicList ->
characteristicList.split(",").map { characteristic ->
Cas1SpaceBookingCharacteristic.entries.first { it.value == characteristic }
}
} ?: emptyList()
}
3 changes: 0 additions & 3 deletions src/main/resources/static/cas1-schemas.yml
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,6 @@ components:
- canonicalArrivalDate
- canonicalDepartureDate
- releaseType
- spaceType
x-enum-varnames:
- PERSON_NAME
- TIER
Expand Down Expand Up @@ -597,9 +596,7 @@ components:
- person
- canonicalArrivalDate
- canonicalDepartureDate
- tier
- spaceType
- releaseType
- essentialCharacteristics
Cas1SpaceBookingSummaryStatus:
type: string
Expand Down
3 changes: 0 additions & 3 deletions src/main/resources/static/codegen/built-cas1-api-spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6577,7 +6577,6 @@ components:
- canonicalArrivalDate
- canonicalDepartureDate
- releaseType
- spaceType
x-enum-varnames:
- PERSON_NAME
- TIER
Expand Down Expand Up @@ -6644,9 +6643,7 @@ components:
- person
- canonicalArrivalDate
- canonicalDepartureDate
- tier
- spaceType
- releaseType
- essentialCharacteristics
Cas1SpaceBookingSummaryStatus:
type: string
Expand Down
Loading

0 comments on commit baa0041

Please sign in to comment.