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 637eeb14c3..a1950c7b4b 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 @@ -3,12 +3,12 @@ package fr.gouv.cnsp.monitorfish.domain.entities.prior_notification import fr.gouv.cnsp.monitorfish.domain.entities.logbook.Catch import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookTripGear import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookTripSegment +import fr.gouv.cnsp.monitorfish.domain.entities.vessel.Vessel data class PriorNotification( val id: Long, val expectedArrivalDate: String?, val expectedLandingDate: String?, - val isVesselUnderCharter: Boolean?, val notificationTypeLabel: String? = null, val onboardCatches: List, val portLocode: String?, @@ -20,18 +20,10 @@ data class PriorNotification( val tripGears: List, val tripSegments: List, val types: List, - val vesselId: Int, - val vesselExternalReferenceNumber: String?, - // ISO Alpha-2 country code - val vesselFlagCountryCode: String?, - val vesselInternalReferenceNumber: String?, - val vesselIrcs: String?, + val vessel: Vessel, val vesselLastControlDate: String?, - val vesselLength: Double?, - val vesselMmsi: String?, - val vesselName: String?, + val vesselRiskFactor: Double?, val vesselRiskFactorImpact: Double?, val vesselRiskFactorProbability: Double?, val vesselRiskFactorDetectability: Double?, - val vesselRiskFactor: Double?, ) 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 e170b78c2a..a605ead9a6 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 @@ -1,6 +1,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.port.Port import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotification import fr.gouv.cnsp.monitorfish.domain.exceptions.CodeNotFoundException import fr.gouv.cnsp.monitorfish.domain.filters.LogbookReportFilter @@ -27,21 +28,13 @@ class GetPriorNotifications( null } - val seaFront = port?.latitude?.let { latitude -> - port.longitude?.let { longitude -> - val point = GeometryFactory().createPoint(Coordinate(longitude, latitude)) + val seaFront = getSeaFrontFromPort(port) - facadeAreasRepository.findByIncluding(point).firstOrNull()?.facade - } - } - - val reportingsCount = priorNotification.vesselId.let { vesselId -> - reportingRepository.findCurrentAndArchivedByVesselIdEquals( - vesselId, - // TODO Fix that. - fromDate = ZonedDateTime.now().minusYears(2), - ).count() - } + val reportingsCount = reportingRepository.findCurrentAndArchivedByVesselIdEquals( + priorNotification.vessel.id, + // TODO Fix that. + fromDate = ZonedDateTime.now().minusYears(2), + ).count() priorNotification.copy( portName = port?.name, @@ -52,4 +45,14 @@ class GetPriorNotifications( return priorNotifications } + + private fun getSeaFrontFromPort(port: Port?): String? { + if (port?.latitude == null || port.longitude == null) { + return null + } + + val point = GeometryFactory().createPoint(Coordinate(port.longitude, port.latitude)) + + return facadeAreasRepository.findByIncluding(point).firstOrNull()?.facade + } } diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationController.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationController.kt index e38a7f1247..a3d9e8eabb 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationController.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationController.kt @@ -1,8 +1,8 @@ package fr.gouv.cnsp.monitorfish.infrastructure.api.bff -import fr.gouv.cnsp.monitorfish.domain.filters.LogbookReportFilter import fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification.GetPriorNotificationTypes import fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification.GetPriorNotifications +import fr.gouv.cnsp.monitorfish.infrastructure.api.input.LogbookReportFilterDataInput import fr.gouv.cnsp.monitorfish.infrastructure.api.outputs.PriorNotificationDataOutput import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.tags.Tag @@ -21,9 +21,11 @@ class PriorNotificationController( @GetMapping("") @Operation(summary = "Get all prior notifications") fun getAll( - @ModelAttribute filter: LogbookReportFilter, + @ModelAttribute filter: LogbookReportFilterDataInput, ): List { - return getPriorNotifications.execute(filter).map { PriorNotificationDataOutput.fromPriorNotification(it) } + return getPriorNotifications.execute(filter.toLogbookReportFilter()).map { + PriorNotificationDataOutput.fromPriorNotification(it) + } } @GetMapping("/types") diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/input/LogbookReportFilterDataInput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/input/LogbookReportFilterDataInput.kt new file mode 100644 index 0000000000..da2cf8b336 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/input/LogbookReportFilterDataInput.kt @@ -0,0 +1,33 @@ +package fr.gouv.cnsp.monitorfish.infrastructure.api.input + +import fr.gouv.cnsp.monitorfish.domain.filters.LogbookReportFilter + +class LogbookReportFilterDataInput( + val flagStates: List? = null, + val isLessThanTwelveMetersVessel: Boolean? = null, + val lastControlledAfter: String? = null, + val lastControlledBefore: String? = null, + val portLocodes: List? = null, + val priorNotificationTypes: List? = null, + val searchQuery: String? = null, + val specyCodes: List? = null, + val tripSegmentSegments: List? = null, + val tripGearCodes: List? = null, + val willArriveAfter: String? = null, + val willArriveBefore: String? = null, +) { + fun toLogbookReportFilter() = LogbookReportFilter( + flagStates = this.flagStates, + isLessThanTwelveMetersVessel = this.isLessThanTwelveMetersVessel, + lastControlledAfter = this.lastControlledAfter, + lastControlledBefore = this.lastControlledBefore, + portLocodes = this.portLocodes, + priorNotificationTypes = this.priorNotificationTypes, + searchQuery = this.searchQuery, + specyCodes = this.specyCodes, + tripSegmentSegments = this.tripSegmentSegments, + tripGearCodes = this.tripGearCodes, + willArriveAfter = this.willArriveAfter, + willArriveBefore = this.willArriveBefore, + ) +} diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/PriorNotificationDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/PriorNotificationDataOutput.kt index eca5601bd0..86123d0fc0 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/PriorNotificationDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/PriorNotificationDataOutput.kt @@ -6,7 +6,6 @@ class PriorNotificationDataOutput( val id: Long, val expectedArrivalDate: String?, val expectedLandingDate: String?, - val isVesselUnderCharter: Boolean?, val notificationTypeLabel: String?, val onBoardCatches: List, val portLocode: String?, @@ -18,15 +17,8 @@ class PriorNotificationDataOutput( val tripGears: List, val tripSegments: List, val types: List, - val vesselId: Int?, - val vesselExternalReferenceNumber: String?, - val vesselFlagCountryCode: String?, - val vesselInternalReferenceNumber: String?, - val vesselIrcs: String?, + val vessel: VesselDataOutput, val vesselLastControlDate: String?, - val vesselLength: Double?, - val vesselMmsi: String?, - val vesselName: String?, val vesselRiskFactorImpact: Double?, val vesselRiskFactorProbability: Double?, val vesselRiskFactorDetectability: Double?, @@ -48,7 +40,6 @@ class PriorNotificationDataOutput( id = priorNotification.id, expectedArrivalDate = priorNotification.expectedArrivalDate, expectedLandingDate = priorNotification.expectedLandingDate, - isVesselUnderCharter = priorNotification.isVesselUnderCharter, notificationTypeLabel = priorNotification.notificationTypeLabel, onBoardCatches, portLocode = priorNotification.portLocode, @@ -60,15 +51,8 @@ class PriorNotificationDataOutput( tripGears, tripSegments, types, - vesselId = priorNotification.vesselId, - vesselExternalReferenceNumber = priorNotification.vesselExternalReferenceNumber, - vesselFlagCountryCode = priorNotification.vesselFlagCountryCode, - vesselInternalReferenceNumber = priorNotification.vesselInternalReferenceNumber, - vesselIrcs = priorNotification.vesselIrcs, + vessel = VesselDataOutput.fromVessel(priorNotification.vessel), vesselLastControlDate = priorNotification.vesselLastControlDate, - vesselLength = priorNotification.vesselLength, - vesselMmsi = priorNotification.vesselMmsi, - vesselName = priorNotification.vesselName, vesselRiskFactorImpact = priorNotification.vesselRiskFactorImpact, vesselRiskFactorProbability = priorNotification.vesselRiskFactorProbability, vesselRiskFactorDetectability = priorNotification.vesselRiskFactorDetectability, 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 c6dd02981a..c8f222110c 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 @@ -165,7 +165,6 @@ data class LogbookReportEntity( id = id!!, expectedArrivalDate = expectedArrivalDate, expectedLandingDate = expectedLandingDate, - isVesselUnderCharter = vessel?.underCharter, types = types, onboardCatches = onboardCatches, portLocode = portLocode, @@ -173,15 +172,8 @@ data class LogbookReportEntity( sentAt = reportDateTime.toString(), tripGears = tripGears, tripSegments = tripSegments, - vesselId = vessel!!.id, - vesselExternalReferenceNumber = vessel.externalReferenceNumber, - vesselFlagCountryCode = vessel.flagState, - vesselInternalReferenceNumber = vessel.internalReferenceNumber, - vesselIrcs = vessel.ircs, - vesselMmsi = vessel.mmsi, + vessel = vessel!!.toVessel(), vesselLastControlDate = vesselRiskFactor?.lastControlDatetime?.toString(), - vesselLength = vessel.length, - vesselName = vessel.vesselName, vesselRiskFactor = vesselRiskFactor?.riskFactor, vesselRiskFactorDetectability = vesselRiskFactor?.detectabilityRiskFactor, vesselRiskFactorImpact = vesselRiskFactor?.impactRiskFactor, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepository.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepository.kt index 09d72a004e..9442e4df43 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepository.kt @@ -14,13 +14,15 @@ import fr.gouv.cnsp.monitorfish.infrastructure.database.entities.LogbookReportEn import fr.gouv.cnsp.monitorfish.infrastructure.database.entities.RiskFactorsEntity import fr.gouv.cnsp.monitorfish.infrastructure.database.repositories.interfaces.DBLogbookReportRepository import jakarta.persistence.EntityManager +import jakarta.persistence.criteria.CriteriaBuilder import jakarta.persistence.criteria.Join +import jakarta.persistence.criteria.Predicate +import jakarta.persistence.criteria.Root import jakarta.transaction.Transactional import org.springframework.beans.factory.annotation.Autowired import org.springframework.cache.annotation.Cacheable import org.springframework.dao.EmptyResultDataAccessException import org.springframework.data.domain.PageRequest -import org.springframework.data.jpa.domain.Specification import org.springframework.data.jpa.repository.Modifying import org.springframework.stereotype.Repository import java.time.ZoneOffset.UTC @@ -35,51 +37,13 @@ class JpaLogbookReportRepository( ) : LogbookReportRepository { private val postgresChunkSize = 5000 - companion object { - fun withIsLessThanTwelveMetersVessel( - isLessThanTwelveMetersVessel: Boolean, - ): Specification { - return Specification { root, _, criteriaBuilder -> - val vessel: Join = root.join("vessel") - - // TODO Double-check if it's < 12 VS >= 12. - if (isLessThanTwelveMetersVessel) { - criteriaBuilder.lessThan(vessel.get("length"), 12) - } else { - criteriaBuilder.greaterThanOrEqualTo(vessel.get("length"), 12) - } - } - } - - fun withLastControlledAfter(lastControlledAfter: String): Specification { - return Specification { root, _, criteriaBuilder -> - val vesselRiskFactor: Join = root.join("vesselRiskFactor") - - criteriaBuilder.greaterThanOrEqualTo( - vesselRiskFactor.get("lastControlDatetime"), - ZonedDateTime.parse(lastControlledAfter, DateTimeFormatter.ISO_ZONED_DATE_TIME), - ) - } - } - - fun withLastControlledBefore(lastControlledBefore: String): Specification { - return Specification { root, _, criteriaBuilder -> - val vesselRiskFactor: Join = root.join("vesselRiskFactor") - - criteriaBuilder.lessThanOrEqualTo( - vesselRiskFactor.get("lastControlDatetime"), - ZonedDateTime.parse(lastControlledBefore, DateTimeFormatter.ISO_ZONED_DATE_TIME), - ) - } - } - } - override fun findAllPriorNotifications(filter: LogbookReportFilter): List { val criteriaBuilder = entityManager.criteriaBuilder val criteriaQuery = criteriaBuilder.createQuery(LogbookReportEntity::class.java) val logbookReportEntity = criteriaQuery.from(LogbookReportEntity::class.java) val predicates = mutableListOf(criteriaBuilder.isTrue(criteriaBuilder.literal(true))) + // Only enriched PNO messages predicates.add( criteriaBuilder.and( @@ -88,153 +52,47 @@ class JpaLogbookReportRepository( ), ) - val predictedArrivalDatetimeUtcAsTimestamp = - criteriaBuilder.function( - "jsonb_to_timestamp", - ZonedDateTime::class.java, - logbookReportEntity.get("message"), - criteriaBuilder.literal("predictedArrivalDatetimeUtc"), - ) - filter.willArriveAfter?.let { willArriveAfter -> - predicates.add( - criteriaBuilder.greaterThanOrEqualTo( - predictedArrivalDatetimeUtcAsTimestamp, - ZonedDateTime.parse(willArriveAfter).withZoneSameInstant(UTC), - ), - ) + predicates.add(getWillArriveAfterPredicate(willArriveAfter, criteriaBuilder, logbookReportEntity)) } - filter.willArriveBefore?.let { willArriveBefore -> - predicates.add( - criteriaBuilder.lessThanOrEqualTo( - predictedArrivalDatetimeUtcAsTimestamp, - ZonedDateTime.parse(willArriveBefore).withZoneSameInstant(UTC), - ), - ) + predicates.add(getWillArriveBeforePredicate(willArriveBefore, criteriaBuilder, logbookReportEntity)) } - filter.flagStates?.let { flagStates -> - predicates.add(logbookReportEntity.get("flagState").`in`(flagStates)) + predicates.add(getFlagStatesPredicate(flagStates, logbookReportEntity)) } - filter.isLessThanTwelveMetersVessel?.let { isLessThanTwelveMetersVessel -> predicates.add( - withIsLessThanTwelveMetersVessel(isLessThanTwelveMetersVessel) - .toPredicate(logbookReportEntity, criteriaQuery, criteriaBuilder), + getIsLessThanTwelveMetersVesselPredicate( + isLessThanTwelveMetersVessel, + criteriaBuilder, + logbookReportEntity, + ), ) } - filter.lastControlledAfter?.let { lastControlledAfter -> - predicates.add( - withLastControlledAfter(lastControlledAfter) - .toPredicate(logbookReportEntity, criteriaQuery, criteriaBuilder), - ) + predicates.add(getLastControlledAfterPredicate(lastControlledAfter, criteriaBuilder, logbookReportEntity)) } - filter.lastControlledBefore?.let { lastControlledBefore -> - predicates.add( - withLastControlledBefore(lastControlledBefore) - .toPredicate(logbookReportEntity, criteriaQuery, criteriaBuilder), - ) + predicates.add(getLastControlledBeforePredicate(lastControlledBefore, criteriaBuilder, logbookReportEntity)) } - filter.portLocodes?.let { portLocodes -> - predicates.add( - criteriaBuilder.function( - "jsonb_extract_path_text", - String::class.java, - logbookReportEntity.get("message"), - criteriaBuilder.literal("port"), - ).`in`(portLocodes), - ) + predicates.add(getPortLocodesPredicate(portLocodes, criteriaBuilder, logbookReportEntity)) + } + filter.priorNotificationTypes?.let { types -> + predicates.add(getPriorNotificationTypesPredicate(types, criteriaBuilder, logbookReportEntity)) } - filter.searchQuery?.let { searchQuery -> - val normalizedPath = - criteriaBuilder.lower( - criteriaBuilder.function( - "unaccent", - String::class.java, - logbookReportEntity.get("vesselName"), - ), - ) - val searchQueryPattern = "%${searchQuery.trim()}%" - val normalizedSearchQuery = - criteriaBuilder.lower( - criteriaBuilder.function( - "unaccent", - String::class.java, - criteriaBuilder.literal(searchQueryPattern), - ), - ) - - predicates.add( - criteriaBuilder.like( - normalizedPath, - normalizedSearchQuery, - ), - ) + predicates.add(getSearchQueryPredicate(searchQuery, criteriaBuilder, logbookReportEntity)) } - filter.specyCodes?.let { specyCodes -> - predicates.add( - criteriaBuilder.isTrue( - criteriaBuilder.function( - "jsonb_contains_any", - Boolean::class.java, - logbookReportEntity.get("message"), - criteriaBuilder.literal(arrayOf("catchOnboard")), - criteriaBuilder.literal("species"), - criteriaBuilder.literal(specyCodes.toTypedArray()), - ), - ), - ) + predicates.add(getSpecyCodesPredicate(specyCodes, criteriaBuilder, logbookReportEntity)) } - filter.tripGearCodes?.let { tripGearCodes -> - predicates.add( - criteriaBuilder.isTrue( - criteriaBuilder.function( - "jsonb_contains_any", - Boolean::class.java, - logbookReportEntity.get("tripGears"), - criteriaBuilder.literal(emptyArray()), - criteriaBuilder.literal("gear"), - criteriaBuilder.literal(tripGearCodes.toTypedArray()), - ), - ), - ) + predicates.add(getTripGearCodesPredicate(tripGearCodes, criteriaBuilder, logbookReportEntity)) } - filter.tripSegmentSegments?.let { tripSegmentSegments -> - predicates.add( - criteriaBuilder.isTrue( - criteriaBuilder.function( - "jsonb_contains_any", - Boolean::class.java, - logbookReportEntity.get("tripSegments"), - criteriaBuilder.literal(emptyArray()), - criteriaBuilder.literal("segment"), - criteriaBuilder.literal(tripSegmentSegments.toTypedArray()), - ), - ), - ) - } - - filter.priorNotificationTypes?.let { types -> - predicates.add( - criteriaBuilder.isTrue( - criteriaBuilder.function( - "jsonb_contains_any", - Boolean::class.java, - logbookReportEntity.get("message"), - criteriaBuilder.literal(arrayOf("pnoTypes")), - criteriaBuilder.literal("pnoTypeName"), - criteriaBuilder.literal(types.toTypedArray()), - ), - ), - ) + predicates.add(getTripSegmentSegmentsPredicate(tripSegmentSegments, criteriaBuilder, logbookReportEntity)) } criteriaQuery.select(logbookReportEntity).where(*predicates.toTypedArray()) @@ -511,4 +369,204 @@ class JpaLogbookReportRepository( private fun getAllMessagesExceptionMessage(internalReferenceNumber: String) = "No messages found for the vessel. (internalReferenceNumber: \"$internalReferenceNumber\")" + + private fun getFlagStatesPredicate( + flagStates: List, + logbookReportEntity: Root, + ): Predicate { + return logbookReportEntity.get("flagState").`in`(flagStates) + } + + private fun getIsLessThanTwelveMetersVesselPredicate( + isLessThanTwelveMetersVessel: Boolean, + criteriaBuilder: CriteriaBuilder, + logbookReportEntity: Root, + ): Predicate { + val vessel: Join = logbookReportEntity.join("vessel") + + // TODO Double-check if it's < 12 VS >= 12. + if (isLessThanTwelveMetersVessel) { + return criteriaBuilder.lessThan(vessel.get("length"), 12) + } else { + return criteriaBuilder.greaterThanOrEqualTo(vessel.get("length"), 12) + } + } + + private fun getLastControlledAfterPredicate( + lastControlledAfter: String, + criteriaBuilder: CriteriaBuilder, + logbookReportEntity: Root, + ): Predicate { + val vesselRiskFactor: Join = logbookReportEntity.join( + "vesselRiskFactor", + ) + + return criteriaBuilder.greaterThanOrEqualTo( + vesselRiskFactor.get("lastControlDatetime"), + ZonedDateTime.parse(lastControlledAfter, DateTimeFormatter.ISO_ZONED_DATE_TIME), + ) + } + + private fun getLastControlledBeforePredicate( + lastControlledBefore: String, + criteriaBuilder: CriteriaBuilder, + logbookReportEntity: Root, + ): Predicate { + val vesselRiskFactor: Join = logbookReportEntity.join( + "vesselRiskFactor", + ) + + return criteriaBuilder.lessThanOrEqualTo( + vesselRiskFactor.get("lastControlDatetime"), + ZonedDateTime.parse(lastControlledBefore, DateTimeFormatter.ISO_ZONED_DATE_TIME), + ) + } + + private fun getPortLocodesPredicate( + portLocodes: List, + criteriaBuilder: CriteriaBuilder, + logbookReportEntity: Root, + ): Predicate { + return criteriaBuilder.function( + "jsonb_extract_path_text", + String::class.java, + logbookReportEntity.get("message"), + criteriaBuilder.literal("port"), + ).`in`(portLocodes) + } + + private fun getPriorNotificationTypesPredicate( + types: List, + criteriaBuilder: CriteriaBuilder, + logbookReportEntity: Root, + ): Predicate { + return criteriaBuilder.isTrue( + criteriaBuilder.function( + "jsonb_contains_any", + Boolean::class.java, + logbookReportEntity.get("message"), + criteriaBuilder.literal(arrayOf("pnoTypes")), + criteriaBuilder.literal("pnoTypeName"), + criteriaBuilder.literal(types.toTypedArray()), + ), + ) + } + + private fun getSearchQueryPredicate( + searchQuery: String, + criteriaBuilder: CriteriaBuilder, + logbookReportEntity: Root, + ): Predicate { + val normalizedPath = + criteriaBuilder.lower( + criteriaBuilder.function( + "unaccent", + String::class.java, + logbookReportEntity.get("vesselName"), + ), + ) + val searchQueryPattern = "%${searchQuery.trim()}%" + val normalizedSearchQuery = + criteriaBuilder.lower( + criteriaBuilder.function( + "unaccent", + String::class.java, + criteriaBuilder.literal(searchQueryPattern), + ), + ) + + return criteriaBuilder.like( + normalizedPath, + normalizedSearchQuery, + ) + } + + private fun getSpecyCodesPredicate( + specyCodes: List, + criteriaBuilder: CriteriaBuilder, + logbookReportEntity: Root, + ): Predicate { + return criteriaBuilder.isTrue( + criteriaBuilder.function( + "jsonb_contains_any", + Boolean::class.java, + logbookReportEntity.get("message"), + criteriaBuilder.literal(arrayOf("catchOnboard")), + criteriaBuilder.literal("species"), + criteriaBuilder.literal(specyCodes.toTypedArray()), + ), + ) + } + + private fun getTripGearCodesPredicate( + tripGearCodes: List, + criteriaBuilder: CriteriaBuilder, + logbookReportEntity: Root, + ): Predicate { + return criteriaBuilder.isTrue( + criteriaBuilder.function( + "jsonb_contains_any", + Boolean::class.java, + logbookReportEntity.get("tripGears"), + criteriaBuilder.literal(emptyArray()), + criteriaBuilder.literal("gear"), + criteriaBuilder.literal(tripGearCodes.toTypedArray()), + ), + ) + } + + private fun getTripSegmentSegmentsPredicate( + tripSegmentSegments: List, + criteriaBuilder: CriteriaBuilder, + logbookReportEntity: Root, + ): Predicate { + return criteriaBuilder.isTrue( + criteriaBuilder.function( + "jsonb_contains_any", + Boolean::class.java, + logbookReportEntity.get("tripSegments"), + criteriaBuilder.literal(emptyArray()), + criteriaBuilder.literal("segment"), + criteriaBuilder.literal(tripSegmentSegments.toTypedArray()), + ), + ) + } + + private fun getWillArriveAfterPredicate( + willArriveAfter: String, + criteriaBuilder: CriteriaBuilder, + logbookReportEntity: Root, + ): Predicate { + val predictedArrivalDatetimeUtcAsTimestamp = + criteriaBuilder.function( + "jsonb_to_timestamp", + ZonedDateTime::class.java, + logbookReportEntity.get("message"), + criteriaBuilder.literal("predictedArrivalDatetimeUtc"), + ) + + return criteriaBuilder.greaterThanOrEqualTo( + predictedArrivalDatetimeUtcAsTimestamp, + ZonedDateTime.parse(willArriveAfter).withZoneSameInstant(UTC), + ) + } + + private fun getWillArriveBeforePredicate( + willArriveBefore: String, + criteriaBuilder: CriteriaBuilder, + logbookReportEntity: Root, + ): Predicate { + val predictedArrivalDatetimeUtcAsTimestamp = + criteriaBuilder.function( + "jsonb_to_timestamp", + ZonedDateTime::class.java, + logbookReportEntity.get("message"), + criteriaBuilder.literal("predictedArrivalDatetimeUtc"), + ) + + return criteriaBuilder.lessThanOrEqualTo( + predictedArrivalDatetimeUtcAsTimestamp, + ZonedDateTime.parse(willArriveBefore).withZoneSameInstant(UTC), + ) + } } diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationTypesUTests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationTypesUTests.kt index f55d04cb0a..a0461a230c 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationTypesUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationTypesUTests.kt @@ -1,8 +1,6 @@ package fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification import com.nhaarman.mockitokotlin2.given -import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotification -import fr.gouv.cnsp.monitorfish.domain.filters.LogbookReportFilter import fr.gouv.cnsp.monitorfish.domain.repositories.* import org.assertj.core.api.Assertions import org.junit.jupiter.api.Test @@ -12,98 +10,20 @@ import org.springframework.test.context.junit.jupiter.SpringExtension @ExtendWith(SpringExtension::class) class GetPriorNotificationTypesUTests { - @MockBean - private lateinit var facadeAreasRepository: FacadeAreasRepository - @MockBean private lateinit var logbookReportRepository: LogbookReportRepository - @MockBean - private lateinit var portRepository: PortRepository - - @MockBean - private lateinit var reportingRepository: ReportingRepository - @Test fun `execute Should return a list of prior notification types`() { // Given - given(logbookReportRepository.findAllPriorNotifications(LogbookReportFilter())).willReturn( - listOf( - PriorNotification( - id = 1, - expectedArrivalDate = null, - expectedLandingDate = null, - isVesselUnderCharter = null, - notificationTypeLabel = null, - onboardCatches = emptyList(), - portLocode = null, - portName = null, - purposeCode = null, - reportingsCount = null, - seaFront = null, - sentAt = null, - tripGears = emptyList(), - tripSegments = emptyList(), - types = emptyList(), - vesselId = 1, - vesselExternalReferenceNumber = null, - vesselFlagCountryCode = null, - vesselInternalReferenceNumber = null, - vesselIrcs = null, - vesselLastControlDate = null, - vesselLength = null, - vesselMmsi = null, - vesselName = null, - vesselRiskFactorImpact = null, - vesselRiskFactorProbability = null, - vesselRiskFactorDetectability = null, - vesselRiskFactor = null, - ), - - PriorNotification( - id = 2, - expectedArrivalDate = null, - expectedLandingDate = null, - isVesselUnderCharter = null, - notificationTypeLabel = null, - onboardCatches = emptyList(), - portLocode = null, - portName = null, - purposeCode = null, - reportingsCount = null, - seaFront = null, - sentAt = null, - tripGears = emptyList(), - tripSegments = emptyList(), - types = emptyList(), - vesselId = 2, - vesselExternalReferenceNumber = null, - vesselFlagCountryCode = null, - vesselInternalReferenceNumber = null, - vesselIrcs = null, - vesselLastControlDate = null, - vesselLength = null, - vesselMmsi = null, - vesselName = null, - vesselRiskFactorImpact = null, - vesselRiskFactorProbability = null, - vesselRiskFactorDetectability = null, - vesselRiskFactor = null, - ), - ), + given(logbookReportRepository.findDistinctPriorNotificationTypes()).willReturn( + listOf("Préavis de Type A", "Préavis de Type B"), ) // When - val result = GetPriorNotifications( - facadeAreasRepository, - logbookReportRepository, - portRepository, - reportingRepository, - ).execute(LogbookReportFilter()) + val result = GetPriorNotificationTypes(logbookReportRepository).execute() // Then - Assertions.assertThat(result).hasSize(2) - Assertions.assertThat(result[0].id).isEqualTo(1) - Assertions.assertThat(result[1].id).isEqualTo(2) + Assertions.assertThat(result).isEqualTo(listOf("Préavis de Type A", "Préavis de Type B")) } } diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationsUTests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationsUTests.kt index d6e1539d07..f1d18c6aac 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationsUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotificationsUTests.kt @@ -1,6 +1,10 @@ package fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification +import com.neovisionaries.i18n.CountryCode import com.nhaarman.mockitokotlin2.given +import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotification +import fr.gouv.cnsp.monitorfish.domain.entities.vessel.Vessel +import fr.gouv.cnsp.monitorfish.domain.filters.LogbookReportFilter import fr.gouv.cnsp.monitorfish.domain.repositories.* import org.assertj.core.api.Assertions import org.junit.jupiter.api.Test @@ -10,20 +14,102 @@ import org.springframework.test.context.junit.jupiter.SpringExtension @ExtendWith(SpringExtension::class) class GetPriorNotificationsUTests { + @MockBean + private lateinit var facadeAreasRepository: FacadeAreasRepository + @MockBean private lateinit var logbookReportRepository: LogbookReportRepository + @MockBean + private lateinit var portRepository: PortRepository + + @MockBean + private lateinit var reportingRepository: ReportingRepository + @Test fun `execute Should return a list of prior notifications`() { // Given - given(logbookReportRepository.findDistinctPriorNotificationTypes()).willReturn( - listOf("Préavis de Type A", "Préavis de Type B"), + given(logbookReportRepository.findAllPriorNotifications(LogbookReportFilter())).willReturn( + listOf( + PriorNotification( + id = 1, + expectedArrivalDate = null, + expectedLandingDate = null, + notificationTypeLabel = null, + onboardCatches = emptyList(), + portLocode = null, + portName = null, + purposeCode = null, + reportingsCount = null, + seaFront = null, + sentAt = null, + tripGears = emptyList(), + tripSegments = emptyList(), + types = emptyList(), + vessel = Vessel( + id = 1, + externalReferenceNumber = null, + flagState = CountryCode.FR, + internalReferenceNumber = null, + ircs = null, + length = null, + mmsi = null, + underCharter = null, + vesselName = null, + ), + vesselLastControlDate = null, + vesselRiskFactorImpact = null, + vesselRiskFactorProbability = null, + vesselRiskFactorDetectability = null, + vesselRiskFactor = null, + ), + + PriorNotification( + id = 2, + expectedArrivalDate = null, + expectedLandingDate = null, + notificationTypeLabel = null, + onboardCatches = emptyList(), + portLocode = null, + portName = null, + purposeCode = null, + reportingsCount = null, + seaFront = null, + sentAt = null, + tripGears = emptyList(), + tripSegments = emptyList(), + types = emptyList(), + vessel = Vessel( + id = 1, + externalReferenceNumber = null, + flagState = CountryCode.UK, + internalReferenceNumber = null, + ircs = null, + length = null, + mmsi = null, + underCharter = null, + vesselName = null, + ), + vesselLastControlDate = null, + vesselRiskFactorImpact = null, + vesselRiskFactorProbability = null, + vesselRiskFactorDetectability = null, + vesselRiskFactor = null, + ), + ), ) // When - val result = GetPriorNotificationTypes(logbookReportRepository).execute() + val result = GetPriorNotifications( + facadeAreasRepository, + logbookReportRepository, + portRepository, + reportingRepository, + ).execute(LogbookReportFilter()) // Then - Assertions.assertThat(result).isEqualTo(listOf("Préavis de Type A", "Préavis de Type B")) + Assertions.assertThat(result).hasSize(2) + Assertions.assertThat(result[0].id).isEqualTo(1) + Assertions.assertThat(result[1].id).isEqualTo(2) } } diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerITests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerITests.kt index b6959dc0f4..728cf18023 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/PriorNotificationControllerITests.kt @@ -1,9 +1,11 @@ package fr.gouv.cnsp.monitorfish.infrastructure.api.bff +import com.neovisionaries.i18n.CountryCode import fr.gouv.cnsp.monitorfish.config.OIDCProperties import fr.gouv.cnsp.monitorfish.config.SecurityConfig import fr.gouv.cnsp.monitorfish.config.SentryConfig import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotification +import fr.gouv.cnsp.monitorfish.domain.entities.vessel.Vessel import fr.gouv.cnsp.monitorfish.domain.filters.LogbookReportFilter import fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification.GetPriorNotificationTypes import fr.gouv.cnsp.monitorfish.domain.use_cases.prior_notification.GetPriorNotifications @@ -40,7 +42,6 @@ class PriorNotificationControllerITests { id = 1, expectedArrivalDate = null, expectedLandingDate = null, - isVesselUnderCharter = null, notificationTypeLabel = null, onboardCatches = emptyList(), portLocode = null, @@ -52,15 +53,18 @@ class PriorNotificationControllerITests { tripGears = emptyList(), tripSegments = emptyList(), types = emptyList(), - vesselId = 1, - vesselExternalReferenceNumber = null, - vesselFlagCountryCode = null, - vesselInternalReferenceNumber = null, - vesselIrcs = null, + vessel = Vessel( + id = 1, + externalReferenceNumber = null, + flagState = CountryCode.FR, + internalReferenceNumber = null, + ircs = null, + length = null, + mmsi = null, + underCharter = null, + vesselName = null, + ), vesselLastControlDate = null, - vesselLength = null, - vesselMmsi = null, - vesselName = null, vesselRiskFactorImpact = null, vesselRiskFactorProbability = null, vesselRiskFactorDetectability = null, @@ -71,7 +75,6 @@ class PriorNotificationControllerITests { id = 2, expectedArrivalDate = null, expectedLandingDate = null, - isVesselUnderCharter = null, notificationTypeLabel = null, onboardCatches = emptyList(), portLocode = null, @@ -83,15 +86,18 @@ class PriorNotificationControllerITests { tripGears = emptyList(), tripSegments = emptyList(), types = emptyList(), - vesselId = 2, - vesselExternalReferenceNumber = null, - vesselFlagCountryCode = null, - vesselInternalReferenceNumber = null, - vesselIrcs = null, + vessel = Vessel( + id = 2, + externalReferenceNumber = null, + flagState = CountryCode.FR, + internalReferenceNumber = null, + ircs = null, + length = null, + mmsi = null, + underCharter = null, + vesselName = null, + ), vesselLastControlDate = null, - vesselLength = null, - vesselMmsi = null, - vesselName = null, vesselRiskFactorImpact = null, vesselRiskFactorProbability = null, vesselRiskFactorDetectability = null, diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepositoryITests.kt index 6c7a185671..8c4f82cfe9 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepositoryITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaLogbookReportRepositoryITests.kt @@ -1,5 +1,6 @@ package fr.gouv.cnsp.monitorfish.infrastructure.database.repositories +import com.neovisionaries.i18n.CountryCode import fr.gouv.cnsp.monitorfish.config.MapperConfiguration import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageTypeMapping import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookOperationType @@ -607,7 +608,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { // Then assertThat(result).hasSizeGreaterThan(0) - assertThat(result.all { listOf("ES", "FR").contains(it.vesselFlagCountryCode) }).isEqualTo(true) + assertThat(result.all { listOf(CountryCode.ES, CountryCode.FR).contains(it.vessel.flagState) }).isEqualTo(true) } @Test @@ -621,7 +622,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { // Then assertThat(firstResult).hasSizeGreaterThan(0) - assertThat(firstResult.all { it.vesselLength!! < 12 }).isEqualTo(true) + assertThat(firstResult.all { it.vessel.length!! < 12 }).isEqualTo(true) // Given val secondFilter = LogbookReportFilter(isLessThanTwelveMetersVessel = false) @@ -631,7 +632,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { // Then assertThat(secondResult).hasSizeGreaterThan(0) - assertThat(secondResult.all { it.vesselLength!! >= 12 }).isEqualTo(true) + assertThat(secondResult.all { it.vessel.length!! >= 12 }).isEqualTo(true) } @Test @@ -691,7 +692,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { // Then assertThat(firstResult).hasSizeGreaterThan(0) - assertThat(firstResult.all { it.vesselName == "PHENOMENE" }).isEqualTo(true) + assertThat(firstResult.all { it.vessel.vesselName == "PHENOMENE" }).isEqualTo(true) // Given val secondFilter = LogbookReportFilter(searchQuery = "hénO") @@ -701,7 +702,7 @@ class JpaLogbookReportRepositoryITests : AbstractDBTests() { // Then assertThat(secondResult).hasSizeGreaterThan(0) - assertThat(secondResult.all { it.vesselName == "PHENOMENE" }).isEqualTo(true) + assertThat(secondResult.all { it.vessel.vesselName == "PHENOMENE" }).isEqualTo(true) } @Test diff --git a/frontend/src/domain/entities/vessel/types.ts b/frontend/src/domain/entities/vessel/types.ts index a79d5d3f78..1331b690ff 100644 --- a/frontend/src/domain/entities/vessel/types.ts +++ b/frontend/src/domain/entities/vessel/types.ts @@ -1,8 +1,8 @@ // TODO This should be moved to `entities/vessel/types.ts` -import type { RiskFactor } from './riskFactor/types' import type { ReportingType } from '../../types/reporting' import type { VesselTrackDepth } from '../vesselTrackDepth' +import type { Vessel } from '@features/Vessel/Vessel.types' import type Feature from 'ol/Feature' import type LineString from 'ol/geom/LineString' import type Point from 'ol/geom/Point' @@ -59,42 +59,7 @@ export type VesselIdentity = { vesselName?: string | null } -export type Vessel = { - beaconNumber: number | null - declaredFishingGears: string[] - district: string - districtCode: string - externalReferenceNumber: string - flagState: string - gauge: number - imo: string - internalReferenceNumber: string - ircs: string - length: number - mmsi: string - navigationLicenceExpirationDate: string - operatorEmails: string[] - operatorName: string - operatorPhones: string[] - pinger: boolean - power: number - proprietorEmails: string[] - proprietorName: string - proprietorPhones: string[] - registryPort: string - riskFactor: RiskFactor - sailingCategory: string - sailingType: string - underCharter: boolean - vesselEmails: string[] - vesselId: number - vesselName: string - vesselPhones: string[] - vesselType: string - width: number -} - -export type SelectedVessel = VesselEnhancedObject & Vessel +export type SelectedVessel = VesselEnhancedObject & Vessel.Vessel export type AugmentedSelectedVessel = SelectedVessel & { hasAlert: boolean @@ -103,7 +68,7 @@ export type AugmentedSelectedVessel = SelectedVessel & { export type VesselAndPositions = { positions: VesselPosition[] - vessel: Vessel + vessel: Vessel.Vessel } export type VesselLastPosition = { diff --git a/frontend/src/domain/entities/vessel/vessel.ts b/frontend/src/domain/entities/vessel/vessel.ts index bf136fd408..f11f012156 100644 --- a/frontend/src/domain/entities/vessel/vessel.ts +++ b/frontend/src/domain/entities/vessel/vessel.ts @@ -6,13 +6,13 @@ import { BaseLayers, LayerProperties } from '../layers/constants' import type { SelectedVessel, ShowedVesselTrack, - Vessel as VesselType, VesselCompositeIdentifier, VesselEnhancedObject, VesselIdentity } from './types' import type { LastPositionVisibility } from '../../types/map' import type { Reporting } from '../../types/reporting' +import type { Vessel as VesselTypes } from '@features/Vessel/Vessel.types' export const VESSEL_ALERT_STYLE = 1 export const VESSEL_INFRACTION_SUSPICION_STYLE = 1 @@ -142,18 +142,18 @@ export class Vessel { } export const getOnlyVesselIdentityProperties = ( - vessel: VesselEnhancedObject | SelectedVessel | VesselType | Reporting + vessel: VesselEnhancedObject | SelectedVessel | VesselTypes.Vessel | Reporting ): VesselIdentity => ({ - beaconNumber: 'beaconNumber' in vessel ? vessel.beaconNumber : null, - districtCode: 'districtCode' in vessel ? vessel.districtCode : null, - externalReferenceNumber: vessel.externalReferenceNumber, + beaconNumber: 'beaconNumber' in vessel && !!vessel.beaconNumber ? vessel.beaconNumber : null, + districtCode: 'districtCode' in vessel && !!vessel.districtCode ? vessel.districtCode : null, + externalReferenceNumber: vessel.externalReferenceNumber ?? null, flagState: vessel.flagState, - internalReferenceNumber: vessel.internalReferenceNumber, - ircs: vessel.ircs, - mmsi: 'mmsi' in vessel ? vessel.mmsi : null, - vesselId: 'vesselId' in vessel ? vessel.vesselId : null, - vesselIdentifier: 'vesselIdentifier' in vessel ? vessel.vesselIdentifier : null, - vesselName: vessel.vesselName + internalReferenceNumber: vessel.internalReferenceNumber ?? null, + ircs: 'ircs' in vessel && !!vessel.ircs ? vessel.ircs : null, + mmsi: 'mmsi' in vessel && !!vessel.mmsi ? vessel.mmsi : null, + vesselId: 'vesselId' in vessel && !!vessel.vesselId ? vessel.vesselId : null, + vesselIdentifier: 'vesselIdentifier' in vessel && !!vessel.vesselIdentifier ? vessel.vesselIdentifier : null, + vesselName: vessel.vesselName ?? null }) export const getVesselCompositeIdentifier: (vessel) => VesselCompositeIdentifier = vessel => diff --git a/frontend/src/features/ActivityReport/types.ts b/frontend/src/features/ActivityReport/types.ts index 1a1ed8a8bb..2e82e7ad11 100644 --- a/frontend/src/features/ActivityReport/types.ts +++ b/frontend/src/features/ActivityReport/types.ts @@ -1,6 +1,6 @@ -import type { Vessel } from '../../domain/entities/vessel/types' import type { LegacyControlUnit } from '../../domain/types/legacyControlUnit' import type { MissionAction } from '../../domain/types/missionAction' +import type { Vessel } from '@features/Vessel/Vessel.types' export type ActivityReports = { activityReports: ActivityReport[] @@ -11,7 +11,7 @@ export type ActivityReport = { action: MissionAction.MissionAction activityCode: ActivityCode controlUnits: LegacyControlUnit.LegacyControlUnit[] - vessel: Vessel + vessel: Vessel.Vessel vesselNationalIdentifier: string } diff --git a/frontend/src/features/Logbook/LogbookMessage.types.ts b/frontend/src/features/Logbook/LogbookMessage.types.ts index 3e1e7e55c1..4470e4afac 100644 --- a/frontend/src/features/Logbook/LogbookMessage.types.ts +++ b/frontend/src/features/Logbook/LogbookMessage.types.ts @@ -1,5 +1,3 @@ -import type { Undefine, UndefineExcept } from '@mtes-mct/monitor-ui' - export namespace LogbookMessage { export type LogbookMessage = { acknowledge: Acknowledge | undefined @@ -32,42 +30,39 @@ export namespace LogbookMessage { returnStatus: string | undefined } - export type Catch = UndefineExcept< - { - conversionFactor: number - economicZone: string - effortZone: string - faoZone: string - freshness: string - numberFish: number - packaging: string - presentation: string - preservationState: string - species: string - speciesName: string - statisticalRectangle: string - weight: number - }, - 'species' - > + export type Catch = { + conversionFactor: number | undefined + economicZone: string | undefined + effortZone: string | undefined + faoZone: string | undefined + freshness: string | undefined + numberFish: number | undefined + packaging: string | undefined + presentation: string | undefined + preservationState: string | undefined + species: string + speciesName: string | undefined + statisticalRectangle: string | undefined + weight: number | undefined + } - export type Message = Undefine<{ - catchOnboard: MessageCatchOnboard[] - economicZone: string - effortZone: string - faoZone: string - latitude: string - longitude: string - pnoTypes: MessagePnoType[] + export type Message = { + catchOnboard: MessageCatchOnboard[] | undefined + economicZone: string | undefined + effortZone: string | undefined + faoZone: string | undefined + latitude: string | undefined + longitude: string | undefined + pnoTypes: MessagePnoType[] | undefined /** Port code. */ - port: string - portName: string - predictedArrivalDatetimeUtc: string - predictedLandingDatetimeUtc: string - purpose: string - statisticalRectangle: string - tripStartDate: string - }> + port: string | undefined + portName: string | undefined + predictedArrivalDatetimeUtc: string | undefined + predictedLandingDatetimeUtc: string | undefined + purpose: string | undefined + statisticalRectangle: string | undefined + tripStartDate: string | undefined + } export type MessageCatchOnboard = { conversionFactor: number @@ -104,21 +99,19 @@ export namespace LogbookMessage { name: string } - export type ApiFilter = Partial< - Undefine<{ - flagStates: string[] - isLessThanTwelveMetersVessel: boolean - lastControlledAfter: string - lastControlledBefore: string - portLocodes: string[] - priorNotificationTypesAsOptions: string[] - searchQuery: string - specyCodes: string[] - tripGearCodes: string[] - tripSegmentSegments: string[] - vesselLength: number - willArriveAfter: string - willArriveBefore: string - }> - > + export type ApiFilter = Partial<{ + flagStates: string[] | undefined + isLessThanTwelveMetersVessel: boolean | undefined + lastControlledAfter: string | undefined + lastControlledBefore: string | undefined + portLocodes: string[] | undefined + priorNotificationTypesAsOptions: string[] | undefined + searchQuery: string | undefined + specyCodes: string[] | undefined + tripGearCodes: string[] | undefined + tripSegmentSegments: string[] | undefined + vesselLength: number | undefined + willArriveAfter: string | undefined + willArriveBefore: string | undefined + }> } diff --git a/frontend/src/features/PriorNotification/PriorNotification.types.ts b/frontend/src/features/PriorNotification/PriorNotification.types.ts index 5448f15ca3..9b382fac33 100644 --- a/frontend/src/features/PriorNotification/PriorNotification.types.ts +++ b/frontend/src/features/PriorNotification/PriorNotification.types.ts @@ -1,5 +1,5 @@ -// import type { SeaFrontGroup } from '../../domain/entities/seaFront/constants' import type { LogbookMessage } from '@features/Logbook/LogbookMessage.types' +import type { Vessel } from '@features/Vessel/Vessel.types' import type { SeaFront } from 'domain/entities/seaFront/constants' export namespace PriorNotification { @@ -19,16 +19,9 @@ export namespace PriorNotification { tripGears: LogbookMessage.TripGear[] tripSegments: LogbookMessage.TripSegment[] types: Type[] - vesselExternalReferenceNumber: string | undefined - vesselFlagCountryCode: string | undefined // TODO Wait for vesselId in logbook reports (including "navire inconnu"). - vesselId: number | undefined - vesselInternalReferenceNumber: string | undefined - vesselIrcs: string | undefined + vessel: Vessel.Vessel vesselLastControlDate: string | undefined - vesselLength: number | undefined - vesselMmsi: string | undefined - vesselName: string | undefined vesselRiskFactor: number | undefined vesselRiskFactorDetectability: number | undefined vesselRiskFactorImpact: number | undefined diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationList/constants.tsx b/frontend/src/features/PriorNotification/components/PriorNotificationList/constants.tsx index 626ae9c082..8381f42fef 100644 --- a/frontend/src/features/PriorNotification/components/PriorNotificationList/constants.tsx +++ b/frontend/src/features/PriorNotification/components/PriorNotificationList/constants.tsx @@ -87,7 +87,7 @@ export const PRIOR_NOTIFICATION_TABLE_COLUMNS: Array row.vesselName ?? '-', + accessorFn: row => row.vessel.vesselName ?? '-', cell: (info: CellContext) => ( {info.getValue()} ), diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationList/index.tsx b/frontend/src/features/PriorNotification/components/PriorNotificationList/index.tsx index c171c09b11..4084ae3a39 100644 --- a/frontend/src/features/PriorNotification/components/PriorNotificationList/index.tsx +++ b/frontend/src/features/PriorNotification/components/PriorNotificationList/index.tsx @@ -208,28 +208,28 @@ export function PriorNotificationList() {

- {!!priorNotification.vesselInternalReferenceNumber && ( + {!!priorNotification.vessel.internalReferenceNumber && ( - {priorNotification.vesselInternalReferenceNumber} (CFR) + {priorNotification.vessel.internalReferenceNumber} (CFR) )} - {!!priorNotification.vesselIrcs && ( + {!!priorNotification.vessel.ircs && ( - {priorNotification.vesselIrcs} (Call sign) + {priorNotification.vessel.ircs} (Call sign) )} - {!!priorNotification.vesselExternalReferenceNumber && ( + {!!priorNotification.vessel.externalReferenceNumber && ( - {priorNotification.vesselExternalReferenceNumber} (Marq. ext.) + {priorNotification.vessel.externalReferenceNumber} (Marq. ext.) )} - {!!priorNotification.vesselMmsi && ( - {priorNotification.vesselMmsi} (MMSI) + {!!priorNotification.vessel.mmsi && ( + {priorNotification.vessel.mmsi} (MMSI) )}

Taille du navire : - {priorNotification.vesselLength ?? 'Inconnu'} + {priorNotification.vessel.length ?? 'Inconnu'}

Dernier contrôle : diff --git a/frontend/src/features/Vessel/Vessel.types.ts b/frontend/src/features/Vessel/Vessel.types.ts new file mode 100644 index 0000000000..29c2625996 --- /dev/null +++ b/frontend/src/features/Vessel/Vessel.types.ts @@ -0,0 +1,39 @@ +import type { RiskFactor } from '../../domain/entities/vessel/riskFactor/types' + +export namespace Vessel { + export type Vessel = { + beaconNumber: number | undefined + declaredFishingGears: string[] | undefined + district: string | undefined + districtCode: string | undefined + externalReferenceNumber: string | undefined + // TODO What's the value for "Navire inconnu"? + flagState: string + gauge: number | undefined + imo: string | undefined + internalReferenceNumber: string | undefined + ircs: string | undefined + length: number | undefined + mmsi: string | undefined + navigationLicenceExpirationDate: string | undefined + operatorEmails: string[] | undefined + operatorName: string | undefined + operatorPhones: string[] | undefined + pinger: boolean | undefined + power: number | undefined + proprietorEmails: string[] | undefined + proprietorName: string | undefined + proprietorPhones: string[] | undefined + registryPort: string | undefined + riskFactor: RiskFactor | undefined + sailingCategory: string | undefined + sailingType: string | undefined + underCharter: boolean | undefined + vesselEmails: string[] | undefined + vesselId: number | undefined + vesselName: string | undefined + vesselPhones: string[] | undefined + vesselType: string | undefined + width: number | undefined + } +}