Skip to content

Commit 6676dfa

Browse files
authored
Simplification de la logique de récupération des PNOs logbook (#3547)
## Linked issues - Resolve #3543
2 parents d00c97f + 7e52afd commit 6676dfa

File tree

33 files changed

+622
-1118
lines changed

33 files changed

+622
-1118
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ run-back: run-stubbed-apis
2323
docker compose up -d --quiet-pull --wait db keycloak
2424
cd backend && ./gradlew bootRun --args='--spring.profiles.active=local --spring.config.additional-location=$(INFRA_FOLDER)'
2525

26+
run-back-for-cypress: run-stubbed-apis
27+
docker compose up -d --quiet-pull --wait db keycloak
28+
cd backend && MONITORFISH_OIDC_ENABLED=false ./gradlew bootRun --args='--spring.profiles.active=local --spring.config.additional-location=$(INFRA_FOLDER)'
29+
2630
run-back-with-monitorenv: run-monitorenv
2731
docker compose up -d --quiet-pull --wait db
2832
cd backend && MONITORENV_URL=http://localhost:9880 ./gradlew bootRun --args='--spring.profiles.active=local --spring.config.additional-location=$(INFRA_FOLDER)'

backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessage.kt

Lines changed: 0 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import fr.gouv.cnsp.monitorfish.domain.entities.gear.Gear
44
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.*
55
import fr.gouv.cnsp.monitorfish.domain.entities.port.Port
66
import fr.gouv.cnsp.monitorfish.domain.entities.species.Species
7-
import fr.gouv.cnsp.monitorfish.domain.exceptions.EntityConversionException
87
import org.slf4j.LoggerFactory
98
import java.time.ZonedDateTime
109

@@ -44,45 +43,6 @@ data class LogbookMessage(
4443
) {
4544
private val logger = LoggerFactory.getLogger(LogbookMessage::class.java)
4645

47-
/**
48-
* Returns the reference logbook message `reportId` (= the original DAT operation `reportId`).
49-
*/
50-
fun getReferenceReportId(): String? {
51-
return referencedReportId ?: reportId
52-
}
53-
54-
fun <T : LogbookMessageValue> toConsolidatedLogbookMessageAndValue(
55-
relatedLogbookMessages: List<LogbookMessage>,
56-
clazz: Class<T>,
57-
): LogbookMessageAndValue<T> {
58-
if (reportId == null) {
59-
throw EntityConversionException(
60-
"Logbook report $id has no `reportId`. You can only enrich a DAT or an orphan COR operation with a `reportId`.",
61-
)
62-
}
63-
if (operationType !in listOf(LogbookOperationType.DAT, LogbookOperationType.COR)) {
64-
throw EntityConversionException(
65-
"Logbook report $id has operationType '$operationType'. You can only enrich a DAT or an orphan COR operation.",
66-
)
67-
}
68-
69-
val historicallySortedRelatedLogbookMessages = relatedLogbookMessages.sortedBy { it.reportDateTime }
70-
val maybeLastLogbookMessageCorrection = historicallySortedRelatedLogbookMessages
71-
.lastOrNull { it.operationType == LogbookOperationType.COR }
72-
73-
val logbookMessageBase = maybeLastLogbookMessageCorrection ?: this
74-
logbookMessageBase.enrichAcnkowledge(relatedLogbookMessages)
75-
val finalLogbookMessage = logbookMessageBase.copy(
76-
isCorrectedByNewerMessage = false,
77-
isDeleted = historicallySortedRelatedLogbookMessages.any { it.operationType == LogbookOperationType.DEL },
78-
)
79-
80-
return LogbookMessageAndValue(
81-
logbookMessage = finalLogbookMessage,
82-
clazz = clazz,
83-
)
84-
}
85-
8646
fun setAcknowledge(newLogbookMessageAcknowledgement: LogbookMessage) {
8747
val currentAcknowledgement = this.acknowledgment
8848
val newAcknowledgement = newLogbookMessageAcknowledgement.message as Acknowledgment
@@ -172,46 +132,6 @@ data class LogbookMessage(
172132
}
173133
}
174134

175-
private fun enrichAcnkowledge(relatedLogbookMessages: List<LogbookMessage>) {
176-
if (this.transmissionFormat == LogbookTransmissionFormat.FLUX ||
177-
LogbookSoftware.isVisioCapture(software)
178-
) {
179-
this.setAcknowledgeAsSuccessful()
180-
181-
return
182-
}
183-
184-
val historycallyOrderedRetLogbookMessages = relatedLogbookMessages
185-
.filter { it.operationType == LogbookOperationType.RET && it.referencedReportId == reportId }
186-
.sortedBy { it.reportDateTime }
187-
188-
val maybeLastSuccessfulRetLogbookMessage = historycallyOrderedRetLogbookMessages.lastOrNull {
189-
val message = it.message as Acknowledgment
190-
191-
message.returnStatus == RETReturnErrorCode.SUCCESS.number
192-
}
193-
// If there is at least one successful RET message, we consider the report as acknowledged
194-
if (maybeLastSuccessfulRetLogbookMessage != null) {
195-
val lastSucessfulRetMessage = maybeLastSuccessfulRetLogbookMessage.message as Acknowledgment
196-
this.acknowledgment = lastSucessfulRetMessage.also {
197-
it.dateTime = maybeLastSuccessfulRetLogbookMessage.reportDateTime
198-
it.isSuccess = true
199-
}
200-
201-
return
202-
}
203-
204-
// Else we consider the last (failure) RET message as the final acknowledgement
205-
val maybeLastRetLogbookMessage = historycallyOrderedRetLogbookMessages.lastOrNull()
206-
if (maybeLastRetLogbookMessage != null) {
207-
val lastRetMessage = maybeLastRetLogbookMessage.message as Acknowledgment
208-
this.acknowledgment = lastRetMessage.also {
209-
it.dateTime = maybeLastRetLogbookMessage.reportDateTime
210-
it.isSuccess = lastRetMessage.returnStatus == RETReturnErrorCode.SUCCESS.number
211-
}
212-
}
213-
}
214-
215135
private fun enrichAcknowledgeCorrectionAndDeletion(contextLogbookMessages: List<LogbookMessage>) {
216136
val referenceLogbookMessage = findReferencedLogbookMessage(contextLogbookMessages)
217137
val relatedLogbookMessages = filterRelatedLogbookMessages(contextLogbookMessages)

backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/prior_notification/PriorNotification.kt

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ package fr.gouv.cnsp.monitorfish.domain.entities.prior_notification
22

33
import fr.gouv.cnsp.monitorfish.domain.entities.facade.Seafront
44
import fr.gouv.cnsp.monitorfish.domain.entities.gear.Gear
5+
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessage
56
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageAndValue
7+
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.Acknowledgment
68
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.PNO
79
import fr.gouv.cnsp.monitorfish.domain.entities.port.Port
810
import fr.gouv.cnsp.monitorfish.domain.entities.reporting.ReportingType
911
import fr.gouv.cnsp.monitorfish.domain.entities.reporting.filters.ReportingFilter
1012
import fr.gouv.cnsp.monitorfish.domain.entities.risk_factor.VesselRiskFactor
1113
import fr.gouv.cnsp.monitorfish.domain.entities.species.Species
14+
import fr.gouv.cnsp.monitorfish.domain.entities.vessel.UNKNOWN_VESSEL
1215
import fr.gouv.cnsp.monitorfish.domain.entities.vessel.Vessel
1316
import fr.gouv.cnsp.monitorfish.domain.exceptions.BackendInternalErrorCode
1417
import fr.gouv.cnsp.monitorfish.domain.exceptions.NoERSMessagesFound
@@ -136,20 +139,52 @@ data class PriorNotification(
136139
reportingCount = currentReportings?.count() ?: 0
137140
}
138141

142+
fun markAsAcknowledged() {
143+
logbookMessageAndValue = LogbookMessageAndValue(
144+
logbookMessageAndValue.logbookMessage.copy(acknowledgment = Acknowledgment(isSuccess = true)),
145+
PNO::class.java,
146+
)
147+
}
148+
139149
companion object {
140150
private val logger = LoggerFactory.getLogger(PriorNotification::class.java)
141151

152+
fun fromLogbookMessage(logbookMessage: LogbookMessage): PriorNotification {
153+
val logbookMessageAndValue = LogbookMessageAndValue(
154+
logbookMessage = logbookMessage,
155+
clazz = PNO::class.java,
156+
)
157+
158+
return PriorNotification(
159+
reportId = logbookMessage.reportId,
160+
createdAt = logbookMessage.operationDateTime,
161+
didNotFishAfterZeroNotice = false,
162+
isManuallyCreated = false,
163+
logbookMessageAndValue = logbookMessageAndValue,
164+
sentAt = logbookMessageAndValue.logbookMessage.reportDateTime,
165+
updatedAt = logbookMessage.operationDateTime,
166+
167+
// These props need to be calculated in the use case
168+
port = null,
169+
reportingCount = null,
170+
seafront = null,
171+
// For practical reasons `vessel` can't be `null`, so we temporarily set it to "Navire inconnu"
172+
vessel = UNKNOWN_VESSEL,
173+
lastControlDateTime = null,
174+
)
175+
}
176+
142177
/**
143178
* Next initial state of the prior notification once it will be created or updated.
144179
*
145180
* Used within the prior notification form to display the next state of the prior notification in real-time.
146181
*/
147182
fun getNextState(
148-
isInverificationScope: Boolean,
183+
isInVerificationScope: Boolean,
149184
isPartOfControlUnitSubscriptions: Boolean,
150185
): PriorNotificationState {
151186
return when {
152-
isInverificationScope -> PriorNotificationState.PENDING_VERIFICATION
187+
isInVerificationScope -> PriorNotificationState.PENDING_VERIFICATION
153188
isPartOfControlUnitSubscriptions -> PriorNotificationState.AUTO_SEND_REQUESTED
154189
else -> PriorNotificationState.OUT_OF_VERIFICATION_SCOPE
155190
}

backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/repositories/LogbookReportRepository.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import fr.gouv.cnsp.monitorfish.domain.exceptions.NoLogbookFishingTripFound
1010
import java.time.ZonedDateTime
1111

1212
interface LogbookReportRepository {
13-
fun findAllPriorNotifications(filter: PriorNotificationsFilter): List<PriorNotification>
13+
fun findAllAcknowledgedPriorNotifications(filter: PriorNotificationsFilter): List<PriorNotification>
1414

1515
@Throws(NoLogbookFishingTripFound::class)
1616
fun findLastTripBeforeDateTime(
@@ -46,7 +46,7 @@ interface LogbookReportRepository {
4646
// Only used in tests
4747
fun findById(id: Long): LogbookMessage
4848

49-
fun findPriorNotificationByReportId(reportId: String, operationDate: ZonedDateTime): PriorNotification?
49+
fun findAcknowledgedPriorNotificationByReportId(reportId: String, operationDate: ZonedDateTime): PriorNotification?
5050

5151
fun findLastMessageDate(): ZonedDateTime
5252

backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotification.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class GetPriorNotification(
2929
val priorNotification = if (isManuallyCreated) {
3030
manualPriorNotificationRepository.findByReportId(reportId)
3131
} else {
32-
logbookReportRepository.findPriorNotificationByReportId(reportId, operationDate)
32+
logbookReportRepository.findAcknowledgedPriorNotificationByReportId(reportId, operationDate)
3333
}
3434
?: throw BackendUsageException(BackendUsageErrorCode.NOT_FOUND)
3535

@@ -55,6 +55,7 @@ class GetPriorNotification(
5555
} else {
5656
null
5757
}
58+
5859
false -> if (priorNotification.logbookMessageAndValue.logbookMessage.internalReferenceNumber != null) {
5960
vesselRepository.findFirstByInternalReferenceNumber(
6061
priorNotification.logbookMessageAndValue.logbookMessage.internalReferenceNumber!!,

backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/GetPriorNotifications.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class GetPriorNotifications(
4343
val allSpecies = speciesRepository.findAll()
4444

4545
val (automaticPriorNotifications, findAllPriorNotificationsTimeTaken) = measureTimedValue {
46-
logbookReportRepository.findAllPriorNotifications(filter)
46+
logbookReportRepository.findAllAcknowledgedPriorNotifications(filter)
4747
}
4848
logger.info(
4949
"TIME_RECORD - 'logbookReportRepository.findAllPriorNotifications()' took $findAllPriorNotificationsTimeTaken.",
@@ -58,11 +58,8 @@ class GetPriorNotifications(
5858

5959
val incompletePriorNotifications = automaticPriorNotifications + manualPriorNotifications
6060

61-
val undeletedPriorNotifications = incompletePriorNotifications
62-
.filter { !it.logbookMessageAndValue.logbookMessage.isDeleted }
63-
6461
val (priorNotifications, enrichedPriorNotificationsTimeTaken) = measureTimedValue {
65-
undeletedPriorNotifications
62+
incompletePriorNotifications
6663
.map { priorNotification ->
6764
priorNotification.enrich(allRiskFactors, allPorts, priorNotification.isManuallyCreated)
6865
priorNotification.logbookMessageAndValue.logbookMessage

backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/PriorNotificationDetailDataOutput.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,22 @@ class PriorNotificationDetailDataOutput(
1919
) {
2020
companion object {
2121
fun fromPriorNotification(priorNotification: PriorNotification): PriorNotificationDetailDataOutput {
22+
val reportId = requireNotNull(priorNotification.reportId) {
23+
"`reportId` is null."
24+
}
25+
2226
val isLessThanTwelveMetersVessel = requireNotNull(priorNotification.vessel) {
2327
"`priorNotification.vessel` is null."
2428
}.isLessThanTwelveMetersVessel()
2529
val isVesselUnderCharter = requireNotNull(priorNotification.vessel) {
2630
"`priorNotification.vessel` is null."
2731
}.underCharter
2832
val logbookMessage = priorNotification.logbookMessageAndValue.logbookMessage
29-
val referenceReportId = requireNotNull(logbookMessage.getReferenceReportId()) {
30-
"`logbookMessage.getReferenceReportId()` returned null."
31-
}
33+
3234
val logbookMessageDataOutput = LogbookMessageDataOutput.fromLogbookMessage(logbookMessage)
3335

3436
return PriorNotificationDetailDataOutput(
35-
reportId = referenceReportId,
37+
reportId,
3638
fingerprint = priorNotification.fingerprint,
3739
isLessThanTwelveMetersVessel = isLessThanTwelveMetersVessel,
3840
isManuallyCreated = priorNotification.isManuallyCreated,

backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/PriorNotificationListItemDataOutput.kt

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import org.slf4j.LoggerFactory
1010
import java.time.ZonedDateTime
1111

1212
data class PriorNotificationListItemDataOutput(
13-
/** Reference logbook message (report) `reportId`. */
14-
val id: String,
13+
val reportId: String,
1514
val acknowledgment: AcknowledgmentDataOutput?,
1615
val createdAt: ZonedDateTime?,
1716
val expectedArrivalDate: ZonedDateTime?,
@@ -51,13 +50,13 @@ data class PriorNotificationListItemDataOutput(
5150
val logger: Logger = LoggerFactory.getLogger(PriorNotificationListItemDataOutput::class.java)
5251

5352
fun fromPriorNotification(priorNotification: PriorNotification): PriorNotificationListItemDataOutput? {
54-
val logbookMessage = priorNotification.logbookMessageAndValue.logbookMessage
55-
val referenceReportId = logbookMessage.getReferenceReportId()
56-
if (referenceReportId == null) {
57-
logger.warn("Prior notification has neither `reportId` nor `referencedReportId`: $priorNotification.")
53+
if (priorNotification.reportId == null) {
54+
logger.warn("Prior notification has no `reportId`: $priorNotification.")
5855

5956
return null
6057
}
58+
59+
val logbookMessage = priorNotification.logbookMessageAndValue.logbookMessage
6160
val message = priorNotification.logbookMessageAndValue.value
6261

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

7574
return PriorNotificationListItemDataOutput(
76-
id = referenceReportId,
75+
reportId = priorNotification.reportId,
7776
acknowledgment = acknowledgment,
7877
createdAt = priorNotification.createdAt,
7978
expectedArrivalDate = message.predictedArrivalDatetimeUtc,

backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LogbookReportEntity.kt

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ package fr.gouv.cnsp.monitorfish.infrastructure.database.entities
22

33
import com.fasterxml.jackson.databind.ObjectMapper
44
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.*
5-
import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.PNO
6-
import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotification
7-
import fr.gouv.cnsp.monitorfish.domain.entities.vessel.UNKNOWN_VESSEL
85
import fr.gouv.cnsp.monitorfish.domain.mappers.ERSMapper.getERSMessageValueFromJSON
96
import io.hypersistence.utils.hibernate.type.json.JsonBinaryType
107
import jakarta.persistence.*
@@ -140,35 +137,6 @@ data class LogbookReportEntity(
140137
)
141138
}
142139

143-
fun toPriorNotification(mapper: ObjectMapper, relatedModels: List<LogbookReportEntity>): PriorNotification {
144-
val referenceLogbookMessage = toLogbookMessage(mapper)
145-
val relatedLogbookMessages = relatedModels
146-
.map { it.toLogbookMessage(mapper) }
147-
.sortedBy { it.operationDateTime }
148-
val consolidatedLogbookMessageAndValue = referenceLogbookMessage
149-
.toConsolidatedLogbookMessageAndValue(relatedLogbookMessages, PNO::class.java)
150-
val updatedAt = relatedLogbookMessages.lastOrNull()?.operationDateTime ?: operationDateTime.atZone(UTC)
151-
// For practical reasons `vessel` can't be `null`, so we temporarily set it to "Navire inconnu"
152-
val vessel = UNKNOWN_VESSEL
153-
154-
return PriorNotification(
155-
reportId = reportId,
156-
createdAt = operationDateTime.atZone(UTC),
157-
didNotFishAfterZeroNotice = false,
158-
isManuallyCreated = false,
159-
logbookMessageAndValue = consolidatedLogbookMessageAndValue,
160-
sentAt = consolidatedLogbookMessageAndValue.logbookMessage.reportDateTime,
161-
updatedAt = updatedAt,
162-
163-
// These props need to be calculated in the use case
164-
port = null,
165-
reportingCount = null,
166-
seafront = null,
167-
vessel = vessel,
168-
lastControlDateTime = null,
169-
)
170-
}
171-
172140
private fun <T> deserializeJSONList(
173141
mapper: ObjectMapper,
174142
json: String?,

0 commit comments

Comments
 (0)