Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Préavis - Amélioration de la vitesse de la liste des PNOs #3525

Merged
merged 2 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading