Skip to content

Commit

Permalink
PI-2098-attendances-filter (#4130)
Browse files Browse the repository at this point in the history
* PI-2098-attendances-filter

* PI-2098: Attendances filter
  • Loading branch information
pmcphee77 authored Aug 6, 2024
1 parent 24e3d51 commit 80e4e3a
Show file tree
Hide file tree
Showing 12 changed files with 293 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,13 @@ class DataLoader(

em.persist(PersonGenerator.PRISON_MANAGER)
em.persist(PersonGenerator.RESPONSIBLE_OFFICER)

em.saveAll(
ContactGenerator.ATTENDANCE_OUTCOME,
ContactGenerator.ATTENDANCE_CONTACT_TYPE,
ContactGenerator.ATTENDANCE_CONTACT_1,
ContactGenerator.ATTENDANCE_CONTACT_2
)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package uk.gov.justice.digital.hmpps.data.generator

import uk.gov.justice.digital.hmpps.integrations.delius.contact.entity.AttendanceOutcome
import uk.gov.justice.digital.hmpps.integrations.delius.contact.entity.Contact
import uk.gov.justice.digital.hmpps.integrations.delius.contact.entity.ContactType
import java.time.LocalDate

object ContactGenerator {
val ATTENDANCE_CONTACT_TYPE = ContactType(
IdGenerator.getAndIncrement(),
"C295",
"Attendance Type",
true,
true
)

val ATTENDANCE_OUTCOME = AttendanceOutcome(
IdGenerator.getAndIncrement(),
"Attendance Outcome"
)

val ATTENDANCE_CONTACT_1 = generateAttendanceContact(
PersonGenerator.CURRENTLY_MANAGED.id,
SentenceGenerator.CURRENTLY_MANAGED.id,
enforcementContact = true
)
val ATTENDANCE_CONTACT_2 = generateAttendanceContact(
PersonGenerator.CURRENTLY_MANAGED.id,
SentenceGenerator.CURRENTLY_MANAGED.id,
enforcementContact = false,
outcome = ATTENDANCE_OUTCOME
)

fun generateAttendanceContact(
personId: Long,
eventId: Long?,
outcome: AttendanceOutcome? = null,
enforcementContact: Boolean? = null
) = Contact(
id = IdGenerator.getAndIncrement(),
type = ATTENDANCE_CONTACT_TYPE,
outcome = outcome,
date = LocalDate.now().minusDays(1),
offenderId = personId,
eventId = eventId,
enforcementContact = enforcementContact
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"attendances": [
{
"attended": false,
"complied": false,
"attendanceDate": "2024-08-05",
"contactId": 145,
"contactType": {
"code": "C295",
"description": "Attendance Type"
}
}
]
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
{
"mappings": [
{
"request": {
"method": "GET",
"urlPathTemplate": "/secure/offenders/crn/{crn}/convictions/{convictionId}/attendancesFilter"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"bodyFileName": "get_attendances_C123456.json"
}
},
{
"request": {
"method": "GET",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package uk.gov.justice.digital.hmpps

import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
Expand All @@ -11,9 +13,11 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import org.springframework.test.web.servlet.result.MockMvcResultHandlers.print
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import uk.gov.justice.digital.hmpps.api.model.Attendances
import uk.gov.justice.digital.hmpps.api.model.KeyValue
import uk.gov.justice.digital.hmpps.api.model.conviction.*
import uk.gov.justice.digital.hmpps.data.generator.AdditionalSentenceGenerator.SENTENCE_DISQ
import uk.gov.justice.digital.hmpps.data.generator.ContactGenerator.ATTENDANCE_CONTACT_1
import uk.gov.justice.digital.hmpps.data.generator.CourtGenerator.BHAM
import uk.gov.justice.digital.hmpps.data.generator.CourtGenerator.PROBATION_AREA
import uk.gov.justice.digital.hmpps.data.generator.DisposalTypeGenerator.CURFEW_ORDER
Expand All @@ -28,6 +32,7 @@ import uk.gov.justice.digital.hmpps.data.generator.SentenceGenerator.CURRENT_SEN
import uk.gov.justice.digital.hmpps.data.generator.SentenceGenerator.MAIN_OFFENCE
import uk.gov.justice.digital.hmpps.data.generator.StaffGenerator.ALLOCATED
import uk.gov.justice.digital.hmpps.data.generator.UnpaidWorkGenerator.UNPAID_WORK_DETAILS_1
import uk.gov.justice.digital.hmpps.integrations.delius.service.toAttendance
import uk.gov.justice.digital.hmpps.test.MockMvcExtensions.contentAsJson
import uk.gov.justice.digital.hmpps.test.MockMvcExtensions.withToken
import java.time.LocalDate
Expand Down Expand Up @@ -253,4 +258,17 @@ internal class ConvictionByCrnAndEventIdIntegrationTest {

assertEquals(expectedResponse.convictionId, response.convictionId)
}

@Test
fun `call convictions by id and attendancesFilter`() {
val crn = PersonGenerator.CURRENTLY_MANAGED.crn
val event = SentenceGenerator.CURRENTLY_MANAGED

val response = mockMvc
.perform(get("/probation-case/$crn/convictions/${event.id}/attendancesFilter").withToken())
.andExpect(status().isOk)
.andReturn().response.contentAsJson<Attendances>()

assertThat(response.attendances[0], equalTo(ATTENDANCE_CONTACT_1.toAttendance()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,10 @@ internal class ProxyIntegrationTest {
"activeOnly": true,
"excludeSoftDeleted": true
},
"CONVICTION_BY_ID_ATTENDANCES": {
"convictionId": "?",
"activeOnly": true
},
"CONVICTION_BY_ID_NSIS": {
"convictionId": "?",
"nsiCodes": "?"
Expand All @@ -249,7 +253,7 @@ internal class ProxyIntegrationTest {
.withToken()
).andExpect(status().is2xxSuccessful).andReturn().response.contentAsJson<CompareAllReport>()

assertThat(res.totalNumberOfRequests, equalTo(9))
assertThat(res.totalNumberOfRequests, equalTo(10))
assertThat(res.totalNumberOfCrns, equalTo(2))
assertThat(res.currentPageNumber, equalTo(1))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package uk.gov.justice.digital.hmpps.api.model

import java.time.LocalDate

data class Attendances(
val attendances: List<Attendance> = emptyList()
)

data class Attendance(
val attended: Boolean,
val complied: Boolean,
val attendanceDate: LocalDate,
val contactId: Long,
val outcome: String?,
val contactType: ContactTypeDetail
)

data class ContactTypeDetail(
val code: String?,
val description: String?
)
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,26 @@ class CommunityApiController(
return proxy(request)
}

@GetMapping("/offenders/crn/{crn}/convictions/{convictionId}/attendancesFilter")
fun convictionByIdAttendances(
request: HttpServletRequest,
@PathVariable crn: String,
@PathVariable convictionId: Long,
): Any {

sendComparisonReport(
mapOf(
"crn" to crn,
"convictionId" to convictionId
), Uri.CONVICTION_BY_ID_ATTENDANCES, request
)

if (featureFlags.enabled("ccd-conviction-by-id-attendances")) {
return convictionResource.getConvictionAttendances(crn, convictionId)
}
return proxy(request)
}

@GetMapping("/offenders/crn/{crn}/convictions/{convictionId}/nsis/{nsiId}")
fun nsisByNisId(
request: HttpServletRequest,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ enum class Uri(
"getNsisByCrnAndConvictionId",
listOf("crn", "convictionId", "nsiCodes"),
),
CONVICTION_BY_ID_ATTENDANCES(
"/secure/offenders/crn/{crn}/convictions/{convictionId}/attendancesFilter",
"convictionResource",
"getConvictionAttendances",
listOf("crn", "convictionId"),
),
CONVICTION_BY_NSIS_ID(
"/secure/offenders/crn/{crn}/convictions/{convictionId}/nsis/{nsiId}",
"convictionResource",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.Parameter
import jakarta.validation.constraints.NotEmpty
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.*
import uk.gov.justice.digital.hmpps.integrations.delius.service.AttendanceService
import uk.gov.justice.digital.hmpps.integrations.delius.service.ConvictionService
import uk.gov.justice.digital.hmpps.integrations.delius.service.InterventionService
import uk.gov.justice.digital.hmpps.integrations.delius.service.RequirementService
Expand All @@ -15,6 +16,7 @@ class ConvictionResource(
private val convictionService: ConvictionService,
private val requirementService: RequirementService,
private val interventionService: InterventionService,
private val attendanceService: AttendanceService
) {

@GetMapping
Expand Down Expand Up @@ -82,4 +84,10 @@ class ConvictionResource(
@PathVariable crn: String,
@PathVariable convictionId: Long
) = requirementService.getPssRequirementsByConvictionId(crn, convictionId)

@GetMapping("/{convictionId}/attendancesFilter")
fun getConvictionAttendances(
@PathVariable crn: String,
@PathVariable convictionId: Long
) = attendanceService.getAttendancesFor(crn, convictionId)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package uk.gov.justice.digital.hmpps.integrations.delius.contact.entity

import jakarta.persistence.*
import org.hibernate.annotations.Immutable
import org.hibernate.type.YesNoConverter
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import java.time.LocalDate

@Immutable
@Entity
@Table(name = "contact")
class Contact(
@Id
@Column(name = "contact_id")
val id: Long = 0,

@Column(updatable = false)
val offenderId: Long,

@ManyToOne
@JoinColumn(name = "contact_type_id")
val type: ContactType? = null,

@Column(name = "contact_date")
val date: LocalDate,

@Column(name = "enforcement", columnDefinition = "number")
val enforcementContact: Boolean? = null,

@Column(name = "event_id")
val eventId: Long? = null,

@Column(name = "attended")
@Convert(converter = YesNoConverter::class)
val attended: Boolean? = null,

@Column(name = "complied")
@Convert(converter = YesNoConverter::class)
val complied: Boolean? = null,

@ManyToOne
@JoinColumn(name = "contact_outcome_type_id")
val outcome: AttendanceOutcome? = null,

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

@Immutable
@Entity
@Table(name = "r_contact_outcome_type")
class AttendanceOutcome(
@Id
@Column(name = "contact_outcome_type_id")
val id: Long,

@Column
val description: String
)

@Immutable
@Entity
@Table(name = "r_contact_type")
class ContactType(
@Id
@Column(name = "contact_type_id")
val id: Long,

val code: String,

val description: String,

@Column(name = "national_standards_contact", length = 1)
@Convert(converter = YesNoConverter::class)
val nationalStandards: Boolean,

@Column(name = "attendance_contact")
@Convert(converter = YesNoConverter::class)
val attendanceContact: Boolean = false,
)

interface AttendanceRepository : JpaRepository<CaseNote, Long> {

@Query(
"""
SELECT contact FROM Contact contact
LEFT OUTER JOIN AttendanceOutcome cot ON cot = contact.outcome
WHERE contact.offenderId = :personId
AND contact.eventId = :eventId
AND contact.date <= :contactDate
AND (contact.enforcementContact = true OR contact.outcome != null)
AND contact.type.attendanceContact = true
AND contact.type.nationalStandards = true
"""
)
fun findByOffenderAndEventId(eventId: Long, personId: Long, contactDate: LocalDate): List<Contact>
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package uk.gov.justice.digital.hmpps.integrations.delius.service

import org.springframework.stereotype.Service
import uk.gov.justice.digital.hmpps.api.model.Attendance
import uk.gov.justice.digital.hmpps.api.model.Attendances
import uk.gov.justice.digital.hmpps.api.model.ContactTypeDetail
import uk.gov.justice.digital.hmpps.integrations.delius.contact.entity.AttendanceRepository
import uk.gov.justice.digital.hmpps.integrations.delius.contact.entity.Contact
import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.PersonRepository
import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.getPerson
import java.time.LocalDate

@Service
class AttendanceService(
private val personRepository: PersonRepository,
private val attendanceRepository: AttendanceRepository
) {

fun getAttendancesFor(crn: String, eventId: Long): Attendances {
val person = personRepository.getPerson(crn)
val attendances =
attendanceRepository.findByOffenderAndEventId(eventId, person.id, LocalDate.now()).map { it.toAttendance() }
return Attendances(attendances)
}
}

fun Contact.toAttendance() = Attendance(
attended = attended ?: false,
complied = complied ?: false,
attendanceDate = date,
contactId = id,
outcome = outcome?.description,
contactType = ContactTypeDetail(code = type?.code, description = type?.description)
)

0 comments on commit 80e4e3a

Please sign in to comment.