Skip to content

Commit

Permalink
PI-2413 Use single active custodial event for prison assessments (#4147)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcus-bcl authored Aug 7, 2024
1 parent 64252bb commit 7284184
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional
import uk.gov.justice.digital.hmpps.data.generator.*
import uk.gov.justice.digital.hmpps.integrations.delius.contact.entity.ContactType
import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.Event
import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.Person
import uk.gov.justice.digital.hmpps.service.Risk
import uk.gov.justice.digital.hmpps.set
Expand Down Expand Up @@ -40,6 +41,7 @@ class DataLoader(
ReferenceDataGenerator.DOMAIN_EVENT_TYPE_DATASET,
*ReferenceDataGenerator.DOMAIN_EVENT_TYPES.toTypedArray()
)
saveAll(ReferenceDataGenerator.DISPOSAL_TYPE)

PersonGenerator.NO_RISK.withEvent().withRisk(Risk.M, Risk.L)
PersonGenerator.LOW_RISK
Expand All @@ -52,12 +54,14 @@ class DataLoader(
PersonGenerator.VERY_HIGH_RISK.withEvent().withRisk(Risk.L, Risk.M, Risk.H)
PersonGenerator.PERSON_NO_EVENT
PersonGenerator.PERSON_SOFT_DELETED_EVENT.withEvent(softDeleted = true).withRisk(Risk.L, Risk.M, Risk.H)
PersonGenerator.PRISON_ASSESSMENT.withEvent(custodial = true)
}

private fun Person.withEvent(softDeleted: Boolean = false): Person {
private fun Person.withEvent(softDeleted: Boolean = false, custodial: Boolean = false): Person {
val personManager = PersonGenerator.generateManager(this)
val event = PersonGenerator.generateEvent(this, softDeleted = softDeleted)
saveAll(this, personManager, event)
if (custodial) event.set(Event::disposal, PersonGenerator.generateDisposal(event))
saveAll(this, personManager, event, event.disposal)
this.set(Person::manager, personManager)
return this
}
Expand Down Expand Up @@ -92,5 +96,5 @@ class DataLoader(
return this
}

private fun saveAll(vararg entities: Any) = entities.forEach(entityManager::merge)
private fun saveAll(vararg entities: Any?) = entities.filterNotNull().forEach(entityManager::merge)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ object PersonGenerator {
val VERY_HIGH_RISK = generate("V123456")
val PERSON_NO_EVENT = generate("E123456")
val PERSON_SOFT_DELETED_EVENT = generate("F123456")
val PRISON_ASSESSMENT = generate("O123456")

fun generate(
crn: String,
Expand All @@ -31,10 +32,19 @@ object PersonGenerator {
fun generateEvent(
person: Person,
number: String = "1",
disposal: Disposal? = null,
active: Boolean = true,
softDeleted: Boolean = false,
id: Long = IdGenerator.getAndIncrement()
) = Event(number, person.id, active, softDeleted, id)
) = Event(number, person.id, disposal, active, softDeleted, id)

fun generateDisposal(
event: Event,
type: DisposalType = ReferenceDataGenerator.DISPOSAL_TYPE,
active: Boolean = true,
softDeleted: Boolean = false,
id: Long = IdGenerator.getAndIncrement()
) = Disposal(event, type, active, softDeleted, id)

fun generateRequirement(
person: Person,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import uk.gov.justice.digital.hmpps.audit.BusinessInteraction
import uk.gov.justice.digital.hmpps.integrations.delius.audit.BusinessInteractionCode
import uk.gov.justice.digital.hmpps.integrations.delius.court.entity.Court
import uk.gov.justice.digital.hmpps.integrations.delius.court.entity.Offence
import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.DisposalType
import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.RequirementMainCategory
import uk.gov.justice.digital.hmpps.integrations.delius.referencedata.entity.Dataset
import uk.gov.justice.digital.hmpps.integrations.delius.referencedata.entity.ReferenceData
Expand All @@ -25,6 +26,7 @@ object ReferenceDataGenerator {
val DOMAIN_EVENT_TYPE_DATASET = generateDataset(Dataset.Code.DOMAIN_EVENT_TYPE.value)
val DOMAIN_EVENT_TYPES = listOf(ReferenceData.Code.REGISTRATION_ADDED, ReferenceData.Code.REGISTRATION_DEREGISTERED)
.map { generateReferenceData(it.value, dataset = DOMAIN_EVENT_TYPE_DATASET) }
val DISPOSAL_TYPE = generateDisposalType("NC")

fun generateReferenceData(
code: String,
Expand All @@ -41,4 +43,7 @@ object ReferenceDataGenerator {
fun generateOffence(code: String, id: Long = IdGenerator.getAndIncrement()) = Offence(code, id)

fun generateReqMainCat(code: String, id: Long = IdGenerator.getAndIncrement()) = RequirementMainCategory(code, id)

fun generateDisposalType(sentenceType: String, id: Long = IdGenerator.getAndIncrement()) =
DisposalType(sentenceType, id)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"Type": "Notification",
"MessageId": "33ad848a-4342-5ff2-9fe9-d96307935317",
"Message": "{\n \"eventType\": \"assessment.summary.produced\",\n \"version\": 1,\n \"description\": \"Assessment Summary has been produced\",\n \"detailUrl\": \"http://localhost:{wiremock.port}/eor/oasys/ass/asssumm/O123456/ALLOW/10069391/COMPLETE\",\n \"occurredAt\": \"2023-12-05T15:32:32+00:00\",\",personReference\":{\"identifiers\":[{\"type\":\"CRN\",\"value\":\"O123456\"}]}}",
"Timestamp": "2023-12-05T15:32:33.823Z",
"MessageAttributes": {
"eventType": {
"Type": "String",
"Value": "risk-assessment.scores.determined"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
{
"probNumber": "O123456",
"assessments": [
{
"assessmentPk": 10069391,
"assessmentType": "LAYER3",
"dateCompleted": "2023-12-07T12:22:44",
"assessorSignedDate": "2023-12-07T12:16:12",
"initiationDate": "2023-12-07T11:49:57",
"assessmentStatus": "COMPLETE",
"superStatus": "COMPLETE",
"laterWIPAssessmentExists": false,
"latestWIPDate": null,
"laterSignLockAssessmentExists": false,
"latestSignLockDate": null,
"laterPartCompUnsignedAssessmentExists": false,
"latestPartCompUnsignedDate": null,
"laterPartCompSignedAssessmentExists": false,
"latestPartCompSignedDate": null,
"laterCompleteAssessmentExists": false,
"latestCompleteDate": "2023-12-07T12:22:44",
"riskChildrenCommunity": null,
"currentConcernsCustody": null,
"currentConcernsDisruptive": null,
"currentConcernsHostel": null,
"reviewNum": null,
"currentConcernsEscape": null,
"initialSpDate": "12/02/2024",
"currentConcernsVulnerablity": null,
"currentConcernsBreachOfTrust": null,
"currentConcernsRiskOfSelfHarm": null,
"currentConcernsRiskOfSuicide": null,
"reviewSpDate": "2024-08-12",
"riskPrisonersCustody": null,
"riskStaffCustody": null,
"riskStaffCommunity": null,
"riskKnownAdultCustody": null,
"riskKnownAdultCommunity": null,
"riskPublicCustody": null,
"riskPublicCommunity": null,
"riskChildrenCustody": null,
"weightedScores": {
"accommodationWeightedScore": 3,
"eteWeightedScore": 3,
"relationshipsWeightedScore": 2,
"lifestyleWeightedScore": 3,
"drugWeightedScore": 0,
"alcoholWeightedScore": 3,
"thinkingWeightedScore": 5,
"attitudesWeightedScore": 4
},
"furtherInformation": {
"totWeightedScore": 76,
"pOAssessment": "270",
"pOAssessmentDesc": "Review",
"assessorName": "John Smith",
"ogrs1Year": 11,
"ogrs2Year": 20,
"reviewTerm": "N",
"courtCode": "CRT150",
"courtType": "MC",
"courtName": "Downpatrick Magistrate Court (DPKMC)"
},
"offender": {
"riskToOthers": "N",
"offenderPk": 7843827
},
"ogpOvp": {
"ogpNC": "Y",
"ovpNC": "Y",
"ogp1Year": null,
"ogp2Year": null,
"ovp1Year": null,
"ovp2Year": null,
"ogrs3RiskRecon": "L",
"ogpRisk": null,
"ovpRisk": null
},
"basicSentencePlan": [
{
"bspAreaLinked": "ATTITUDES",
"bspAreaLinkedDesc": "Attitudes"
},
{
"bspAreaLinked": "RISK_MANAGEMENT",
"bspAreaLinkedDesc": "Risk Management"
},
{
"bspAreaLinked": "THINKING_AND_BEHAVIOUR",
"bspAreaLinkedDesc": "Thinking/Behaviour"
},
{
"bspAreaLinked": "EDUCATION_TRAINING_AND_EMPLOYABILITY",
"bspAreaLinkedDesc": "ETE"
},
{
"bspAreaLinked": "EDUCATION_TRAINING_AND_EMPLOYABILITY",
"bspAreaLinkedDesc": "ETE"
},
{
"bspAreaLinked": "ATTITUDES",
"bspAreaLinkedDesc": "Attitudes"
}
],
"offences": [
{
"offenceCode": "804",
"offenceSubcode": "00",
"additionalOffence": "N"
}
],
"sentencePlan": null
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,19 @@
"status": 200,
"bodyFileName": "assessment-summary-G123456.json"
}
},
{
"request": {
"method": "GET",
"urlPath": "/eor/oasys/ass/asssumm/O123456/ALLOW/10069391/COMPLETE"
},
"response": {
"headers": {
"Content-Type": "application/json"
},
"status": 200,
"bodyFileName": "assessment-summary-O123456.json"
}
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.mockito.ArgumentMatchers
import org.mockito.ArgumentMatchers.anyMap
import org.mockito.kotlin.*
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
Expand Down Expand Up @@ -251,8 +251,8 @@ internal class IntegrationTest {

verify(telemetryService, timeout(5000)).trackEvent(
eq("AssessmentSummaryFailureReport"),
ArgumentMatchers.anyMap(),
ArgumentMatchers.anyMap()
anyMap(),
anyMap()
)
}

Expand All @@ -266,10 +266,10 @@ internal class IntegrationTest {

verify(telemetryService, timeout(5000)).trackEvent(
eq("AssessmentSummaryFailureReport"),
org.mockito.kotlin.check {
check {
assertThat(it["reason"], Matchers.equalTo("Event with number of 1 not found"))
},
ArgumentMatchers.anyMap()
anyMap()
)
}

Expand All @@ -283,10 +283,21 @@ internal class IntegrationTest {

verify(telemetryService, timeout(5000)).trackEvent(
eq("AssessmentSummaryFailureReport"),
org.mockito.kotlin.check {
check {
assertThat(it["reason"], Matchers.equalTo("Person with crn of G123456 not found"))
},
ArgumentMatchers.anyMap()
anyMap()
)
}

@Test
fun `event number is inferred for prison assessments`() {
val message = notification<HmppsDomainEvent>("assessment-summary-produced-O123456")

channelManager.getChannel(queueName).publishAndWait(prepNotification(message, wireMockServer.port()))

verify(telemetryService, timeout(5000)).trackEvent(eq("AssessmentSummarySuccess"), anyMap(), anyMap())
val assessment = oasysAssessmentRepository.findByOasysId("10069391")
assertThat(assessment?.eventNumber, equalTo("1"))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package uk.gov.justice.digital.hmpps.integrations.delius.person.entity

import jakarta.persistence.*
import org.hibernate.annotations.Immutable

@Entity
@Immutable
class Disposal(
@OneToOne
@JoinColumn(name = "event_id")
val event: Event,

@ManyToOne
@JoinColumn(name = "disposal_type_id")
val type: DisposalType,

@Column(name = "active_flag", columnDefinition = "number")
val active: Boolean = true,

@Column(columnDefinition = "number")
val softDeleted: Boolean = false,

@Id
@Column(name = "disposal_id")
val id: Long,
)

@Entity
@Immutable
@Table(name = "r_disposal_type")
class DisposalType(
@Column
val sentenceType: String,

@Id
@Column(name = "disposal_type_id")
val id: Long,
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package uk.gov.justice.digital.hmpps.integrations.delius.person.entity
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.OneToOne
import org.hibernate.annotations.Immutable
import org.hibernate.annotations.SQLRestriction
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import uk.gov.justice.digital.hmpps.exception.IgnorableMessageException

@Immutable
Expand All @@ -19,6 +21,9 @@ class Event(
@Column(name = "offender_id")
val personId: Long,

@OneToOne(mappedBy = "event")
val disposal: Disposal?,

@Column(name = "active_flag", columnDefinition = "number")
val active: Boolean,

Expand All @@ -32,6 +37,17 @@ class Event(

interface EventRepository : JpaRepository<Event, Long> {
fun findByPersonIdAndNumber(personId: Long, number: String): Event?

@Query(
"""
select e.number from Event e
where e.personId = :personId
and e.disposal.type.sentenceType in ('NC', 'SC')
and e.disposal.active = true and e.active = true
and e.disposal.softDeleted = false and e.softDeleted = false
"""
)
fun findActiveCustodialEvents(personId: Long): List<String>
}

fun EventRepository.getByNumber(personId: Long, number: String) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ class AssessmentService(
val previousAssessment = oasysAssessmentRepository.findByOasysId(summary.assessmentPk.toString())

val eventNumber = summary.furtherInformation.cmsEventNumber?.toString()
?: throw IgnorableMessageException("No Event Number provided")
?: eventRepository.findActiveCustodialEvents(person.id).singleOrNull()
?: throw IgnorableMessageException("No single active custodial event")
val event = eventRepository.getByNumber(person.id, eventNumber)
val manager = checkNotNull(person.manager) { "Community Manager Not Found" }
val contactDate =
Expand Down

0 comments on commit 7284184

Please sign in to comment.