Skip to content

Commit

Permalink
Préavis - Amélioration de la vitesse de la liste des PNOs (#3525)
Browse files Browse the repository at this point in the history
## Linked issues

- Resolve #3515 

----

- [ ] Tests E2E (Cypress)
  • Loading branch information
ivangabriele committed Aug 12, 2024
2 parents fd347f8 + 0059204 commit d3e57c0
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 71 deletions.
12 changes: 8 additions & 4 deletions backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ package fr.gouv.cnsp.monitorfish.domain

import java.security.MessageDigest

fun hash(toHash: String) = MessageDigest
.getInstance("SHA-256")
.digest(toHash.toByteArray())
.fold("") { str, it -> str + "%02x".format(it) }
fun hash(toHash: String): String {
val lowercaseToHash = toHash.lowercase()

return MessageDigest
.getInstance("SHA-256")
.digest(lowercaseToHash.toByteArray())
.fold("") { str, it -> str + "%02x".format(it) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ import fr.gouv.cnsp.monitorfish.domain.entities.reporting.ReportingType
import fr.gouv.cnsp.monitorfish.domain.entities.reporting.filters.ReportingFilter
import fr.gouv.cnsp.monitorfish.domain.entities.risk_factor.VesselRiskFactor
import fr.gouv.cnsp.monitorfish.domain.entities.species.Species
import fr.gouv.cnsp.monitorfish.domain.entities.vessel.UNKNOWN_VESSEL
import fr.gouv.cnsp.monitorfish.domain.entities.vessel.Vessel
import fr.gouv.cnsp.monitorfish.domain.exceptions.BackendInternalErrorCode
import fr.gouv.cnsp.monitorfish.domain.exceptions.CodeNotFoundException
import fr.gouv.cnsp.monitorfish.domain.exceptions.NoERSMessagesFound
import fr.gouv.cnsp.monitorfish.domain.repositories.LogbookRawMessageRepository
import fr.gouv.cnsp.monitorfish.domain.repositories.ReportingRepository
Expand All @@ -30,7 +28,7 @@ data class PriorNotification(
var seafront: Seafront?,
val sentAt: ZonedDateTime?,
val updatedAt: ZonedDateTime?,
var vessel: Vessel?,
val vessel: Vessel?,
var lastControlDateTime: ZonedDateTime?,
) {
/** Each prior notification and each of its updates have a unique fingerprint. */
Expand Down Expand Up @@ -73,42 +71,25 @@ data class PriorNotification(
}

fun enrich(
allPorts: List<Port>,
allRiskFactors: List<VesselRiskFactor>,
allVessels: List<Vessel>,
allPorts: List<Port>,
isManuallyCreated: Boolean,
) {
val logbookMessage = logbookMessageAndValue.logbookMessage
val pnoMessage = logbookMessageAndValue.value

port = try {
pnoMessage.port?.let { portLocode ->
allPorts.find { it.locode == portLocode }
}
} catch (e: CodeNotFoundException) {
null
port = pnoMessage.port?.let { portLocode ->
allPorts.find { it.locode == portLocode }
}

seafront = port?.facade?.let { Seafront.from(it) }

// Default to UNKNOWN vessel when null or not found
vessel = if (isManuallyCreated) {
logbookMessage.vesselId?.let { vesselId ->
allVessels.find { it.id == vesselId }
} ?: UNKNOWN_VESSEL
} else {
logbookMessage
.internalReferenceNumber?.let { vesselInternalReferenceNumber ->
allVessels.find { it.internalReferenceNumber == vesselInternalReferenceNumber }
} ?: UNKNOWN_VESSEL
}

lastControlDateTime = if (isManuallyCreated) {
logbookMessage.vesselId?.let { vesselId ->
allRiskFactors.find { it.vesselId == vesselId }?.lastControlDatetime
}
} else {
vessel!!.internalReferenceNumber?.let { vesselInternalReferenceNumber ->
logbookMessage.internalReferenceNumber?.let { vesselInternalReferenceNumber ->
allRiskFactors.find { it.internalReferenceNumber == vesselInternalReferenceNumber }?.lastControlDatetime
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ interface VesselRepository {

fun findVesselsByIds(ids: List<Int>): List<Vessel>

fun findVesselsByInternalReferenceNumbers(internalReferenceNumbers: List<String>): List<Vessel>

fun findVesselById(vesselId: Int): Vessel?

fun search(searched: String): List<Vessel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@ package fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification
import fr.gouv.cnsp.monitorfish.config.UseCase
import fr.gouv.cnsp.monitorfish.domain.entities.facade.SeafrontGroup
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotificationStats
import fr.gouv.cnsp.monitorfish.domain.repositories.*
import fr.gouv.cnsp.monitorfish.domain.repositories.LogbookReportRepository
import fr.gouv.cnsp.monitorfish.domain.repositories.ManualPriorNotificationRepository
import fr.gouv.cnsp.monitorfish.domain.repositories.PortRepository
import fr.gouv.cnsp.monitorfish.domain.repositories.RiskFactorRepository

@UseCase
class GetNumberToVerify(
private val logbookReportRepository: LogbookReportRepository,
private val manualPriorNotificationRepository: ManualPriorNotificationRepository,
private val portRepository: PortRepository,
private val riskFactorRepository: RiskFactorRepository,
private val vesselRepository: VesselRepository,
) {
fun execute(): PriorNotificationStats {
val allPorts = portRepository.findAll()
val allRiskFactors = riskFactorRepository.findAll()
val allVessels = vesselRepository.findAll()

val automaticPriorNotifications = logbookReportRepository.findAllPriorNotificationsToVerify()
val manualPriorNotifications = manualPriorNotificationRepository.findAllToVerify()
Expand All @@ -27,7 +28,7 @@ class GetNumberToVerify(

val priorNotifications = undeletedPriorNotifications
.map { priorNotification ->
priorNotification.enrich(allPorts, allRiskFactors, allVessels, priorNotification.isManuallyCreated)
priorNotification.enrich(allRiskFactors, allPorts, priorNotification.isManuallyCreated)

priorNotification
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification

import fr.gouv.cnsp.monitorfish.config.UseCase
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotification
import fr.gouv.cnsp.monitorfish.domain.entities.vessel.UNKNOWN_VESSEL
import fr.gouv.cnsp.monitorfish.domain.exceptions.BackendUsageErrorCode
import fr.gouv.cnsp.monitorfish.domain.exceptions.BackendUsageException
import fr.gouv.cnsp.monitorfish.domain.repositories.*
Expand All @@ -24,7 +25,6 @@ class GetPriorNotification(
val allPorts = portRepository.findAll()
val allRiskFactors = riskFactorRepository.findAll()
val allSpecies = speciesRepository.findAll()
val allVessels = vesselRepository.findAll()

val priorNotification = if (isManuallyCreated) {
manualPriorNotificationRepository.findByReportId(reportId)
Expand All @@ -33,15 +33,37 @@ class GetPriorNotification(
}
?: throw BackendUsageException(BackendUsageErrorCode.NOT_FOUND)

priorNotification.enrich(allPorts, allRiskFactors, allVessels, isManuallyCreated)
priorNotification.enrich(allRiskFactors, allPorts, isManuallyCreated)
priorNotification.enrichLogbookMessage(
allGears,
allPorts,
allSpecies,
logbookRawMessageRepository,
)
priorNotification.enrichReportingCount(reportingRepository)
val priorNotificationWithVessel = getPriorNotificationWithVessel(priorNotification)

return priorNotification
return priorNotificationWithVessel
}

private fun getPriorNotificationWithVessel(
priorNotification: PriorNotification,
): PriorNotification {
val vessel = when (priorNotification.isManuallyCreated) {
true -> if (priorNotification.logbookMessageAndValue.logbookMessage.vesselId != null) {
vesselRepository.findVesselById(priorNotification.logbookMessageAndValue.logbookMessage.vesselId!!)
} else {
null
}
false -> if (priorNotification.logbookMessageAndValue.logbookMessage.internalReferenceNumber != null) {
vesselRepository.findFirstByInternalReferenceNumber(
priorNotification.logbookMessageAndValue.logbookMessage.internalReferenceNumber!!,
)
} else {
null
}
} ?: UNKNOWN_VESSEL

return priorNotification.copy(vessel = vessel)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotifica
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotificationStats
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.filters.PriorNotificationsFilter
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.sorters.PriorNotificationsSortColumn
import fr.gouv.cnsp.monitorfish.domain.entities.vessel.UNKNOWN_VESSEL
import fr.gouv.cnsp.monitorfish.domain.repositories.*
import fr.gouv.cnsp.monitorfish.domain.utils.PaginatedList
import org.slf4j.Logger
Expand Down Expand Up @@ -36,30 +37,10 @@ class GetPriorNotifications(
pageNumber: Int,
pageSize: Int,
): PaginatedList<PriorNotification, PriorNotificationStats> {
val (allGears, gearRepositoryTimeTaken) = measureTimedValue {
gearRepository.findAll()
}
logger.info("TIME_RECORD - 'gearRepository.findAll()' took $gearRepositoryTimeTaken.")

val (allPorts, portRepositoryTimeTaken) = measureTimedValue {
portRepository.findAll()
}
logger.info("TIME_RECORD - 'portRepository.findAll()' took $portRepositoryTimeTaken.")

val (allRiskFactors, allRiskFactorsTimeTaken) = measureTimedValue {
riskFactorRepository.findAll()
}
logger.info("TIME_RECORD - 'riskFactorRepository.findAll()' took $allRiskFactorsTimeTaken.")

val (allSpecies, speciesRepositoryTimeTaken) = measureTimedValue {
speciesRepository.findAll()
}
logger.info("TIME_RECORD - 'speciesRepository.findAll()' took $speciesRepositoryTimeTaken.")

val (allVessels, vesselRepositoryTimeTaken) = measureTimedValue {
vesselRepository.findAll()
}
logger.info("TIME_RECORD - 'vesselRepository.findAll()' took $vesselRepositoryTimeTaken.")
val allGears = gearRepository.findAll()
val allPorts = portRepository.findAll()
val allRiskFactors = riskFactorRepository.findAll()
val allSpecies = speciesRepository.findAll()

val (automaticPriorNotifications, findAllPriorNotificationsTimeTaken) = measureTimedValue {
logbookReportRepository.findAllPriorNotifications(filter)
Expand All @@ -83,7 +64,7 @@ class GetPriorNotifications(
val (priorNotifications, enrichedPriorNotificationsTimeTaken) = measureTimedValue {
undeletedPriorNotifications
.map { priorNotification ->
priorNotification.enrich(allPorts, allRiskFactors, allVessels, priorNotification.isManuallyCreated)
priorNotification.enrich(allRiskFactors, allPorts, priorNotification.isManuallyCreated)
priorNotification.logbookMessageAndValue.logbookMessage
.enrichGearPortAndSpecyNames(allGears, allPorts, allSpecies)

Expand Down Expand Up @@ -133,9 +114,11 @@ class GetPriorNotifications(
}
logger.info("TIME_RECORD - 'paginatedList' took $paginatedListTimeTaken.")

val paginatedListWithVessels = paginatedList.copy(data = getPriorNotificationsWithVessel(paginatedList.data))

// Enrich the reporting count for each prior notification after pagination to limit the number of queries
val (enrichedPaginatedList, enrichedPaginatedListTimeTaken) = measureTimedValue {
paginatedList.apply {
paginatedListWithVessels.apply {
data.forEach {
it.enrichReportingCount(reportingRepository)
}
Expand All @@ -146,6 +129,35 @@ class GetPriorNotifications(
return enrichedPaginatedList
}

private fun getPriorNotificationsWithVessel(
priorNotifications: List<PriorNotification>,
): List<PriorNotification> {
val vesselsIds = priorNotifications
.filter { it.isManuallyCreated }
.mapNotNull { it.logbookMessageAndValue.logbookMessage.vesselId }
val internalReferenceNumbers = priorNotifications
.filter { !it.isManuallyCreated }
.mapNotNull { it.logbookMessageAndValue.logbookMessage.internalReferenceNumber }
val vessels = vesselRepository.findVesselsByIds(vesselsIds) +
vesselRepository.findVesselsByInternalReferenceNumbers(internalReferenceNumbers)

return priorNotifications.map {
val isManuallyCreated = it.isManuallyCreated
val vesselId = it.logbookMessageAndValue.logbookMessage.vesselId
val internalReferenceNumber = it.logbookMessageAndValue.logbookMessage.internalReferenceNumber

val vessel = vessels.find { searchedVessel ->
return@find if (isManuallyCreated) {
searchedVessel.id == vesselId
} else {
searchedVessel.internalReferenceNumber == internalReferenceNumber
}
} ?: UNKNOWN_VESSEL

return@map it.copy(vessel = vessel)
}
}

companion object {
private fun getSortKey(
priorNotification: PriorNotification,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ data class PaginatedList<Entity, ExtraData>(

return PaginatedList(
data = paginatedItems,
extraData,
lastPageNumber,
pageNumber,
pageSize,
totalLength,
extraData = extraData,
lastPageNumber = lastPageNumber,
pageNumber = pageNumber,
pageSize = pageSize,
totalLength = totalLength,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ data class LogbookReportEntity(
@Column(name = "is_test_message")
val isTestMessage: Boolean = false,

) {
) {
companion object {
fun fromLogbookMessage(
mapper: ObjectMapper,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ class JpaVesselRepository(private val dbVesselRepository: DBVesselRepository) :
return dbVesselRepository.findAllByIds(ids).map { it.toVessel() }
}

override fun findVesselsByInternalReferenceNumbers(internalReferenceNumbers: List<String>): List<Vessel> {
return dbVesselRepository.findAllByInternalReferenceNumbers(internalReferenceNumbers).map { it.toVessel() }
}

override fun findVesselById(vesselId: Int): Vessel? {
return dbVesselRepository.findById(vesselId).getOrNull()?.toVessel()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ interface DBVesselRepository : CrudRepository<VesselEntity, Int> {
@Query(value = "SELECT * FROM vessels WHERE id in (:ids)", nativeQuery = true)
fun findAllByIds(ids: List<Int>): List<VesselEntity>

@Query(value = "SELECT * FROM vessels WHERE cfr in (:cfr)", nativeQuery = true)
fun findAllByInternalReferenceNumbers(cfr: List<String>): List<VesselEntity>

@Query(
value = """
SELECT under_charter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ class GetNumberToVerifyUTests {
@MockBean
private lateinit var riskFactorRepository: RiskFactorRepository

@MockBean
private lateinit var vesselRepository: VesselRepository

@Test
fun `execute Should return a map of PNO for each facade to verify`() {
// Given
Expand Down Expand Up @@ -118,7 +115,6 @@ class GetNumberToVerifyUTests {
manualPriorNotificationRepository,
portRepository,
riskFactorRepository,
vesselRepository,
).execute()

// Then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,18 @@ class JpaVesselRepositoryITests : AbstractDBTests() {
assertThat(vessels.first().vesselName).isEqualTo("PHENOMENE")
}

@Test
@Transactional
fun `findVesselsByInternalReferenceNumbers Should return vessels from vessel CFR`() {
// When
val vessels = jpaVesselRepository.findVesselsByInternalReferenceNumbers(listOf("U_W0NTFINDME", "FAK000999999"))

// Then
assertThat(vessels).hasSize(2)
assertThat(vessels.first().internalReferenceNumber).isEqualTo("FAK000999999")
assertThat(vessels.first().vesselName).isEqualTo("PHENOMENE")
}

@Test
@Transactional
fun `findUnderCharterForVessel Should get the underCharter field of a vessel`() {
Expand Down

0 comments on commit d3e57c0

Please sign in to comment.