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

Simplification de la logique de récupération des PNOs logbook #3547

Merged
merged 26 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9982dd0
Flag acknowledged prior notifications in pno list query
VincentAntoine Aug 14, 2024
2ec872b
Update get logbook pno by report_id query
VincentAntoine Aug 14, 2024
4e9924b
Update query
VincentAntoine Aug 14, 2024
f8e3c61
Remove logbook prior notification consolidation in Backend
ivangabriele Aug 14, 2024
6bd2b7f
Add chain resolution in JpaLogbookReportRepository.findAllPriorNotifi…
ivangabriele Aug 15, 2024
d34b3db
Add missing RETs in pno logbook reports test data
ivangabriele Aug 15, 2024
21f81f1
Remove uncovered prior notification update cases in Backend unit tests
ivangabriele Aug 15, 2024
9d2c15c
Remove unused code in Backend
ivangabriele Aug 15, 2024
40eee0b
Add chain resolution in JpaLogbookReportRepository.findPriorNotificat…
ivangabriele Aug 15, 2024
76e29e4
Clarify naming in JpaLogbookReportRepository
ivangabriele Aug 15, 2024
68a414b
Fix prior notification list reportIds in Backend
ivangabriele Aug 15, 2024
9390ca2
Comment duplicate vessel CFR test data case
ivangabriele Aug 15, 2024
fb996fa
Rename prior notification list item id to report id in Frontend
ivangabriele Aug 15, 2024
59dd6e6
Remove non-acknowldged prior notification e2e test case
ivangabriele Aug 15, 2024
007dd3b
Fix logbook prior notification update in Backend
ivangabriele Aug 15, 2024
29f856a
Fix e2e tests replacing non-acknowledged & right reportId
ivangabriele Aug 15, 2024
71845b3
Add dev commands for Cypress
ivangabriele Aug 15, 2024
ef12e2c
Restore missing UTC fix in JpaLogbookReportRepository
ivangabriele Aug 15, 2024
7d7a0df
Fix prior notification auto-save e2e test reset
ivangabriele Aug 15, 2024
86d802f
Fix prior notification update e2e test reset
ivangabriele Aug 15, 2024
e22c544
Restore VisioCapture value in logbook report test data
ivangabriele Aug 15, 2024
49fe62a
Fix prior reset in prior notifiaction update e2e test
ivangabriele Aug 15, 2024
098efbc
Simplify jpa repository
louptheron Aug 16, 2024
97455b1
Add acknowledgment
louptheron Aug 16, 2024
d544248
Improve perf
louptheron Aug 16, 2024
7e52afd
Add acknowledgment of single pno
louptheron Aug 16, 2024
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
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ run-back: run-stubbed-apis
docker compose up -d --quiet-pull --wait db keycloak
cd backend && ./gradlew bootRun --args='--spring.profiles.active=local --spring.config.additional-location=$(INFRA_FOLDER)'

run-back-for-cypress: run-stubbed-apis
docker compose up -d --quiet-pull --wait db keycloak
cd backend && MONITORFISH_OIDC_ENABLED=false ./gradlew bootRun --args='--spring.profiles.active=local --spring.config.additional-location=$(INFRA_FOLDER)'

run-back-with-monitorenv: run-monitorenv
docker compose up -d --quiet-pull --wait db
cd backend && MONITORENV_URL=http://localhost:9880 ./gradlew bootRun --args='--spring.profiles.active=local --spring.config.additional-location=$(INFRA_FOLDER)'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import fr.gouv.cnsp.monitorfish.domain.entities.gear.Gear
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.*
import fr.gouv.cnsp.monitorfish.domain.entities.port.Port
import fr.gouv.cnsp.monitorfish.domain.entities.species.Species
import fr.gouv.cnsp.monitorfish.domain.exceptions.EntityConversionException
import org.slf4j.LoggerFactory
import java.time.ZonedDateTime

Expand Down Expand Up @@ -44,45 +43,6 @@ data class LogbookMessage(
) {
private val logger = LoggerFactory.getLogger(LogbookMessage::class.java)

/**
* Returns the reference logbook message `reportId` (= the original DAT operation `reportId`).
*/
fun getReferenceReportId(): String? {
return referencedReportId ?: reportId
}

fun <T : LogbookMessageValue> toConsolidatedLogbookMessageAndValue(
relatedLogbookMessages: List<LogbookMessage>,
clazz: Class<T>,
): LogbookMessageAndValue<T> {
if (reportId == null) {
throw EntityConversionException(
"Logbook report $id has no `reportId`. You can only enrich a DAT or an orphan COR operation with a `reportId`.",
)
}
if (operationType !in listOf(LogbookOperationType.DAT, LogbookOperationType.COR)) {
throw EntityConversionException(
"Logbook report $id has operationType '$operationType'. You can only enrich a DAT or an orphan COR operation.",
)
}

val historicallySortedRelatedLogbookMessages = relatedLogbookMessages.sortedBy { it.reportDateTime }
val maybeLastLogbookMessageCorrection = historicallySortedRelatedLogbookMessages
.lastOrNull { it.operationType == LogbookOperationType.COR }

val logbookMessageBase = maybeLastLogbookMessageCorrection ?: this
logbookMessageBase.enrichAcnkowledge(relatedLogbookMessages)
val finalLogbookMessage = logbookMessageBase.copy(
isCorrectedByNewerMessage = false,
isDeleted = historicallySortedRelatedLogbookMessages.any { it.operationType == LogbookOperationType.DEL },
)

return LogbookMessageAndValue(
logbookMessage = finalLogbookMessage,
clazz = clazz,
)
}

fun setAcknowledge(newLogbookMessageAcknowledgement: LogbookMessage) {
val currentAcknowledgement = this.acknowledgment
val newAcknowledgement = newLogbookMessageAcknowledgement.message as Acknowledgment
Expand Down Expand Up @@ -172,46 +132,6 @@ data class LogbookMessage(
}
}

private fun enrichAcnkowledge(relatedLogbookMessages: List<LogbookMessage>) {
if (this.transmissionFormat == LogbookTransmissionFormat.FLUX ||
LogbookSoftware.isVisioCapture(software)
) {
this.setAcknowledgeAsSuccessful()

return
}

val historycallyOrderedRetLogbookMessages = relatedLogbookMessages
.filter { it.operationType == LogbookOperationType.RET && it.referencedReportId == reportId }
.sortedBy { it.reportDateTime }

val maybeLastSuccessfulRetLogbookMessage = historycallyOrderedRetLogbookMessages.lastOrNull {
val message = it.message as Acknowledgment

message.returnStatus == RETReturnErrorCode.SUCCESS.number
}
// If there is at least one successful RET message, we consider the report as acknowledged
if (maybeLastSuccessfulRetLogbookMessage != null) {
val lastSucessfulRetMessage = maybeLastSuccessfulRetLogbookMessage.message as Acknowledgment
this.acknowledgment = lastSucessfulRetMessage.also {
it.dateTime = maybeLastSuccessfulRetLogbookMessage.reportDateTime
it.isSuccess = true
}

return
}

// Else we consider the last (failure) RET message as the final acknowledgement
val maybeLastRetLogbookMessage = historycallyOrderedRetLogbookMessages.lastOrNull()
if (maybeLastRetLogbookMessage != null) {
val lastRetMessage = maybeLastRetLogbookMessage.message as Acknowledgment
this.acknowledgment = lastRetMessage.also {
it.dateTime = maybeLastRetLogbookMessage.reportDateTime
it.isSuccess = lastRetMessage.returnStatus == RETReturnErrorCode.SUCCESS.number
}
}
}

private fun enrichAcknowledgeCorrectionAndDeletion(contextLogbookMessages: List<LogbookMessage>) {
val referenceLogbookMessage = findReferencedLogbookMessage(contextLogbookMessages)
val relatedLogbookMessages = filterRelatedLogbookMessages(contextLogbookMessages)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ package fr.gouv.cnsp.monitorfish.domain.entities.prior_notification

import fr.gouv.cnsp.monitorfish.domain.entities.facade.Seafront
import fr.gouv.cnsp.monitorfish.domain.entities.gear.Gear
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessage
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageAndValue
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.Acknowledgment
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.PNO
import fr.gouv.cnsp.monitorfish.domain.entities.port.Port
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.NoERSMessagesFound
Expand Down Expand Up @@ -136,20 +139,52 @@ data class PriorNotification(
reportingCount = currentReportings?.count() ?: 0
}

fun markAsAcknowledged() {
logbookMessageAndValue = LogbookMessageAndValue(
logbookMessageAndValue.logbookMessage.copy(acknowledgment = Acknowledgment(isSuccess = true)),
PNO::class.java,
)
}

companion object {
private val logger = LoggerFactory.getLogger(PriorNotification::class.java)

fun fromLogbookMessage(logbookMessage: LogbookMessage): PriorNotification {
val logbookMessageAndValue = LogbookMessageAndValue(
logbookMessage = logbookMessage,
clazz = PNO::class.java,
)

return PriorNotification(
reportId = logbookMessage.reportId,
createdAt = logbookMessage.operationDateTime,
didNotFishAfterZeroNotice = false,
isManuallyCreated = false,
logbookMessageAndValue = logbookMessageAndValue,
sentAt = logbookMessageAndValue.logbookMessage.reportDateTime,
updatedAt = logbookMessage.operationDateTime,

// These props need to be calculated in the use case
port = null,
reportingCount = null,
seafront = null,
// For practical reasons `vessel` can't be `null`, so we temporarily set it to "Navire inconnu"
vessel = UNKNOWN_VESSEL,
lastControlDateTime = null,
)
}

/**
* Next initial state of the prior notification once it will be created or updated.
*
* Used within the prior notification form to display the next state of the prior notification in real-time.
*/
fun getNextState(
isInverificationScope: Boolean,
isInVerificationScope: Boolean,
isPartOfControlUnitSubscriptions: Boolean,
): PriorNotificationState {
return when {
isInverificationScope -> PriorNotificationState.PENDING_VERIFICATION
isInVerificationScope -> PriorNotificationState.PENDING_VERIFICATION
isPartOfControlUnitSubscriptions -> PriorNotificationState.AUTO_SEND_REQUESTED
else -> PriorNotificationState.OUT_OF_VERIFICATION_SCOPE
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import fr.gouv.cnsp.monitorfish.domain.exceptions.NoLogbookFishingTripFound
import java.time.ZonedDateTime

interface LogbookReportRepository {
fun findAllPriorNotifications(filter: PriorNotificationsFilter): List<PriorNotification>
fun findAllAcknowledgedPriorNotifications(filter: PriorNotificationsFilter): List<PriorNotification>

@Throws(NoLogbookFishingTripFound::class)
fun findLastTripBeforeDateTime(
Expand Down Expand Up @@ -46,7 +46,7 @@ interface LogbookReportRepository {
// Only used in tests
fun findById(id: Long): LogbookMessage

fun findPriorNotificationByReportId(reportId: String, operationDate: ZonedDateTime): PriorNotification?
fun findAcknowledgedPriorNotificationByReportId(reportId: String, operationDate: ZonedDateTime): PriorNotification?

fun findLastMessageDate(): ZonedDateTime

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class GetPriorNotification(
val priorNotification = if (isManuallyCreated) {
manualPriorNotificationRepository.findByReportId(reportId)
} else {
logbookReportRepository.findPriorNotificationByReportId(reportId, operationDate)
logbookReportRepository.findAcknowledgedPriorNotificationByReportId(reportId, operationDate)
}
?: throw BackendUsageException(BackendUsageErrorCode.NOT_FOUND)

Expand All @@ -55,6 +55,7 @@ class GetPriorNotification(
} else {
null
}

false -> if (priorNotification.logbookMessageAndValue.logbookMessage.internalReferenceNumber != null) {
vesselRepository.findFirstByInternalReferenceNumber(
priorNotification.logbookMessageAndValue.logbookMessage.internalReferenceNumber!!,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class GetPriorNotifications(
val allSpecies = speciesRepository.findAll()

val (automaticPriorNotifications, findAllPriorNotificationsTimeTaken) = measureTimedValue {
logbookReportRepository.findAllPriorNotifications(filter)
logbookReportRepository.findAllAcknowledgedPriorNotifications(filter)
}
logger.info(
"TIME_RECORD - 'logbookReportRepository.findAllPriorNotifications()' took $findAllPriorNotificationsTimeTaken.",
Expand All @@ -58,11 +58,8 @@ class GetPriorNotifications(

val incompletePriorNotifications = automaticPriorNotifications + manualPriorNotifications

val undeletedPriorNotifications = incompletePriorNotifications
.filter { !it.logbookMessageAndValue.logbookMessage.isDeleted }

val (priorNotifications, enrichedPriorNotificationsTimeTaken) = measureTimedValue {
undeletedPriorNotifications
incompletePriorNotifications
.map { priorNotification ->
priorNotification.enrich(allRiskFactors, allPorts, priorNotification.isManuallyCreated)
priorNotification.logbookMessageAndValue.logbookMessage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,22 @@ class PriorNotificationDetailDataOutput(
) {
companion object {
fun fromPriorNotification(priorNotification: PriorNotification): PriorNotificationDetailDataOutput {
val reportId = requireNotNull(priorNotification.reportId) {
"`reportId` is null."
}

val isLessThanTwelveMetersVessel = requireNotNull(priorNotification.vessel) {
"`priorNotification.vessel` is null."
}.isLessThanTwelveMetersVessel()
val isVesselUnderCharter = requireNotNull(priorNotification.vessel) {
"`priorNotification.vessel` is null."
}.underCharter
val logbookMessage = priorNotification.logbookMessageAndValue.logbookMessage
val referenceReportId = requireNotNull(logbookMessage.getReferenceReportId()) {
"`logbookMessage.getReferenceReportId()` returned null."
}

val logbookMessageDataOutput = LogbookMessageDataOutput.fromLogbookMessage(logbookMessage)

return PriorNotificationDetailDataOutput(
reportId = referenceReportId,
reportId,
fingerprint = priorNotification.fingerprint,
isLessThanTwelveMetersVessel = isLessThanTwelveMetersVessel,
isManuallyCreated = priorNotification.isManuallyCreated,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import org.slf4j.LoggerFactory
import java.time.ZonedDateTime

data class PriorNotificationListItemDataOutput(
/** Reference logbook message (report) `reportId`. */
val id: String,
val reportId: String,
val acknowledgment: AcknowledgmentDataOutput?,
val createdAt: ZonedDateTime?,
val expectedArrivalDate: ZonedDateTime?,
Expand Down Expand Up @@ -51,13 +50,13 @@ data class PriorNotificationListItemDataOutput(
val logger: Logger = LoggerFactory.getLogger(PriorNotificationListItemDataOutput::class.java)

fun fromPriorNotification(priorNotification: PriorNotification): PriorNotificationListItemDataOutput? {
val logbookMessage = priorNotification.logbookMessageAndValue.logbookMessage
val referenceReportId = logbookMessage.getReferenceReportId()
if (referenceReportId == null) {
logger.warn("Prior notification has neither `reportId` nor `referencedReportId`: $priorNotification.")
if (priorNotification.reportId == null) {
logger.warn("Prior notification has no `reportId`: $priorNotification.")

return null
}

val logbookMessage = priorNotification.logbookMessageAndValue.logbookMessage
val message = priorNotification.logbookMessageAndValue.value

val acknowledgment = logbookMessage.acknowledgment?.let { AcknowledgmentDataOutput.fromAcknowledgment(it) }
Expand All @@ -73,7 +72,7 @@ data class PriorNotificationListItemDataOutput(
val vessel = requireNotNull(priorNotification.vessel) { "`vessel` is null." }

return PriorNotificationListItemDataOutput(
id = referenceReportId,
reportId = priorNotification.reportId,
acknowledgment = acknowledgment,
createdAt = priorNotification.createdAt,
expectedArrivalDate = message.predictedArrivalDatetimeUtc,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ package fr.gouv.cnsp.monitorfish.infrastructure.database.entities

import com.fasterxml.jackson.databind.ObjectMapper
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.*
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.PNO
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.mappers.ERSMapper.getERSMessageValueFromJSON
import io.hypersistence.utils.hibernate.type.json.JsonBinaryType
import jakarta.persistence.*
Expand Down Expand Up @@ -140,35 +137,6 @@ data class LogbookReportEntity(
)
}

fun toPriorNotification(mapper: ObjectMapper, relatedModels: List<LogbookReportEntity>): PriorNotification {
val referenceLogbookMessage = toLogbookMessage(mapper)
val relatedLogbookMessages = relatedModels
.map { it.toLogbookMessage(mapper) }
.sortedBy { it.operationDateTime }
val consolidatedLogbookMessageAndValue = referenceLogbookMessage
.toConsolidatedLogbookMessageAndValue(relatedLogbookMessages, PNO::class.java)
val updatedAt = relatedLogbookMessages.lastOrNull()?.operationDateTime ?: operationDateTime.atZone(UTC)
// For practical reasons `vessel` can't be `null`, so we temporarily set it to "Navire inconnu"
val vessel = UNKNOWN_VESSEL

return PriorNotification(
reportId = reportId,
createdAt = operationDateTime.atZone(UTC),
didNotFishAfterZeroNotice = false,
isManuallyCreated = false,
logbookMessageAndValue = consolidatedLogbookMessageAndValue,
sentAt = consolidatedLogbookMessageAndValue.logbookMessage.reportDateTime,
updatedAt = updatedAt,

// These props need to be calculated in the use case
port = null,
reportingCount = null,
seafront = null,
vessel = vessel,
lastControlDateTime = null,
)
}

private fun <T> deserializeJSONList(
mapper: ObjectMapper,
json: String?,
Expand Down
Loading
Loading