diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/Utils.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/Utils.kt index be89f03794..a08d33930b 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/Utils.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/Utils.kt @@ -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) } +} diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotification.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotification.kt index 6e4a04368e..01f9823e47 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotification.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotification.kt @@ -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 @@ -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. */ @@ -73,42 +71,25 @@ data class PriorNotification( } fun enrich( - allPorts: List, allRiskFactors: List, - allVessels: List, + allPorts: List, 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 } } diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/VesselRepository.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/VesselRepository.kt index 498cdee22b..57aebfb817 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/VesselRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/VesselRepository.kt @@ -16,6 +16,8 @@ interface VesselRepository { fun findVesselsByIds(ids: List): List + fun findVesselsByInternalReferenceNumbers(internalReferenceNumbers: List): List + fun findVesselById(vesselId: Int): Vessel? fun search(searched: String): List diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetNumberToVerify.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetNumberToVerify.kt index 9bb48c0023..5e01d2ac04 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetNumberToVerify.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetNumberToVerify.kt @@ -3,7 +3,10 @@ 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( @@ -11,12 +14,10 @@ class GetNumberToVerify( 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() @@ -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 } diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotification.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotification.kt index 63460f5594..30aba7b34a 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotification.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotification.kt @@ -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.* @@ -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) @@ -33,7 +33,7 @@ class GetPriorNotification( } ?: throw BackendUsageException(BackendUsageErrorCode.NOT_FOUND) - priorNotification.enrich(allPorts, allRiskFactors, allVessels, isManuallyCreated) + priorNotification.enrich(allRiskFactors, allPorts, isManuallyCreated) priorNotification.enrichLogbookMessage( allGears, allPorts, @@ -41,7 +41,29 @@ class GetPriorNotification( 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) } } diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotifications.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotifications.kt index f880c4f327..5e04c42b9f 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotifications.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotifications.kt @@ -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 @@ -36,30 +37,10 @@ class GetPriorNotifications( pageNumber: Int, pageSize: Int, ): PaginatedList { - 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) @@ -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) @@ -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) } @@ -146,6 +129,35 @@ class GetPriorNotifications( return enrichedPaginatedList } + private fun getPriorNotificationsWithVessel( + priorNotifications: List, + ): List { + 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, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/utils/PaginatedList.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/utils/PaginatedList.kt index 6e550600af..1e353ada99 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/utils/PaginatedList.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/utils/PaginatedList.kt @@ -25,11 +25,11 @@ data class PaginatedList( return PaginatedList( data = paginatedItems, - extraData, - lastPageNumber, - pageNumber, - pageSize, - totalLength, + extraData = extraData, + lastPageNumber = lastPageNumber, + pageNumber = pageNumber, + pageSize = pageSize, + totalLength = totalLength, ) } } diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LogbookReportEntity.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LogbookReportEntity.kt index 47c7796406..560043db78 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LogbookReportEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LogbookReportEntity.kt @@ -76,7 +76,7 @@ data class LogbookReportEntity( @Column(name = "is_test_message") val isTestMessage: Boolean = false, - ) { +) { companion object { fun fromLogbookMessage( mapper: ObjectMapper, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaVesselRepository.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaVesselRepository.kt index 20e35de93f..27a5229f9b 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaVesselRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaVesselRepository.kt @@ -64,6 +64,10 @@ class JpaVesselRepository(private val dbVesselRepository: DBVesselRepository) : return dbVesselRepository.findAllByIds(ids).map { it.toVessel() } } + override fun findVesselsByInternalReferenceNumbers(internalReferenceNumbers: List): List { + return dbVesselRepository.findAllByInternalReferenceNumbers(internalReferenceNumbers).map { it.toVessel() } + } + override fun findVesselById(vesselId: Int): Vessel? { return dbVesselRepository.findById(vesselId).getOrNull()?.toVessel() } diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/interfaces/DBVesselRepository.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/interfaces/DBVesselRepository.kt index cd8a10b497..0be6148911 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/interfaces/DBVesselRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/interfaces/DBVesselRepository.kt @@ -29,6 +29,9 @@ interface DBVesselRepository : CrudRepository { @Query(value = "SELECT * FROM vessels WHERE id in (:ids)", nativeQuery = true) fun findAllByIds(ids: List): List + @Query(value = "SELECT * FROM vessels WHERE cfr in (:cfr)", nativeQuery = true) + fun findAllByInternalReferenceNumbers(cfr: List): List + @Query( value = """ SELECT under_charter diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetNumberToVerifyUTests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetNumberToVerifyUTests.kt index 2289832008..ba1a4ba8e8 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetNumberToVerifyUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetNumberToVerifyUTests.kt @@ -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 @@ -118,7 +115,6 @@ class GetNumberToVerifyUTests { manualPriorNotificationRepository, portRepository, riskFactorRepository, - vesselRepository, ).execute() // Then diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaVesselRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaVesselRepositoryITests.kt index f5f7a0b413..b6a31365bf 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaVesselRepositoryITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaVesselRepositoryITests.kt @@ -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`() {