Skip to content

Commit

Permalink
[Alertes] Ajout de l'alerte de FAR manquant pour 48h et de la suspens…
Browse files Browse the repository at this point in the history
…tion d'alertes (#2383)

## Linked issues

- [x] Resolve #2189
- [x] Resolve #1370
- [x] Permettre la mise sous silence des alertes quand il n'y a pas de
façade (considéré comme "Toute façade")
- [x] Passer sur l'API publique `api/v1`
- [x] Enlever `MISSING_FAR_ALERT` de l'API `operational_alerts`
  • Loading branch information
louptheron committed Sep 15, 2023
2 parents 112b506 + 2d2a186 commit 377b7ed
Show file tree
Hide file tree
Showing 84 changed files with 2,020 additions and 1,348 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import fr.gouv.cnsp.monitorfish.domain.entities.alerts.type.AlertType
import fr.gouv.cnsp.monitorfish.domain.entities.vessel.VesselIdentifier
import java.time.ZonedDateTime

class SilencedAlert(
data class SilencedAlert(
val id: Int? = null,
val vesselId: Int? = null,
val vesselName: String? = null,
val internalReferenceNumber: String? = null,
val externalReferenceNumber: String? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ enum class AlertTypeMapping(private val clazz: Class<out AlertType>) : IHasImple
FRENCH_EEZ_FISHING_ALERT(FrenchEEZFishingAlert::class.java),
TWELVE_MILES_FISHING_ALERT(TwelveMilesFishingAlert::class.java),
MISSING_FAR_ALERT(MissingFARAlert::class.java),
MISSING_FAR_48_HOURS_ALERT(MissingFAR48HoursAlert::class.java),
;

override fun getImplementation(): Class<out AlertType> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package fr.gouv.cnsp.monitorfish.domain.entities.alerts.type

class MissingFAR48HoursAlert(
override var seaFront: String? = null,
override var dml: String? = null,
var riskFactor: Double? = null,
) : AlertType(AlertTypeMapping.MISSING_FAR_48_HOURS_ALERT, seaFront, dml, 27689)
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface SilencedAlertRepository {
silencedBeforeDate: ZonedDateTime,
isValidated: Boolean,
): SilencedAlert
fun save(silencedAlert: SilencedAlert): SilencedAlert

fun findAllCurrentSilencedAlerts(): List<SilencedAlert>
fun delete(id: Int)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import fr.gouv.cnsp.monitorfish.domain.repositories.SilencedAlertRepository
import org.slf4j.LoggerFactory

@UseCase
class DeleteSilencedOperationalAlert(private val silencedAlertRepository: SilencedAlertRepository) {
private val logger = LoggerFactory.getLogger(DeleteSilencedOperationalAlert::class.java)
class DeleteSilencedAlert(private val silencedAlertRepository: SilencedAlertRepository) {
private val logger = LoggerFactory.getLogger(DeleteSilencedAlert::class.java)

fun execute(silencedAlertId: Int) {
logger.info("Deleting silenced alert $silencedAlertId.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ import fr.gouv.cnsp.monitorfish.domain.repositories.PendingAlertRepository
import org.slf4j.LoggerFactory

@UseCase
class GetOperationalAlerts(
class GetPendingAlerts(
private val pendingAlertRepository: PendingAlertRepository,
private val infractionRepository: InfractionRepository,
) {
private val logger = LoggerFactory.getLogger(GetOperationalAlerts::class.java)
private val logger = LoggerFactory.getLogger(GetPendingAlerts::class.java)

fun execute(): List<PendingAlert> {
return pendingAlertRepository.findAlertsOfTypes(
listOf(
AlertTypeMapping.THREE_MILES_TRAWLING_ALERT,
AlertTypeMapping.FRENCH_EEZ_FISHING_ALERT,
AlertTypeMapping.TWELVE_MILES_FISHING_ALERT,
AlertTypeMapping.MISSING_FAR_ALERT,
AlertTypeMapping.MISSING_FAR_48_HOURS_ALERT,
),
).map { pendingAlert ->
pendingAlert.value.natinfCode?.let {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package fr.gouv.cnsp.monitorfish.domain.use_cases.alert

import fr.gouv.cnsp.monitorfish.config.UseCase
import fr.gouv.cnsp.monitorfish.domain.entities.alerts.SilencedAlert
import fr.gouv.cnsp.monitorfish.domain.entities.alerts.type.*
import fr.gouv.cnsp.monitorfish.domain.repositories.SilencedAlertRepository
import org.slf4j.LoggerFactory

@UseCase
class SilenceAlert(
private val silencedAlertRepository: SilencedAlertRepository,
) {
private val logger = LoggerFactory.getLogger(SilenceAlert::class.java)

fun execute(silencedAlert: SilencedAlert): SilencedAlert {
// We recreate the object from the `value.type` as the AlertType class from our domain contains the `natinfCode`
val silencedAlertWithNatinf = getSilencedAlertWithNatinf(silencedAlert)

val savedSilencedAlert = silencedAlertRepository.save(silencedAlertWithNatinf)

// If the silenced alert is of type MISSING_FAR_ALERT or MISSING_FAR_48_HOURS_ALERT, we need to save the other silenced alert
when (silencedAlert.value.type) {
AlertTypeMapping.MISSING_FAR_ALERT -> {
// If MISSING_FAR_ALERT was silenced, we also silence MISSING_FAR_48_HOURS_ALERT
val missingFAR48HoursSilencedAlert = silencedAlert.copy(value = MissingFAR48HoursAlert())
silencedAlertRepository.save(missingFAR48HoursSilencedAlert)
}
AlertTypeMapping.MISSING_FAR_48_HOURS_ALERT -> {
// If MISSING_FAR_48_HOURS_ALERT was silenced, we also silence MISSING_FAR_ALERT
val missingFARSilencedAlert = silencedAlert.copy(value = MissingFARAlert())
silencedAlertRepository.save(missingFARSilencedAlert)
}
else -> {}
}

logger.info(
"Alert ${savedSilencedAlert.value.type} has been silenced for vessel " +
"${savedSilencedAlert.internalReferenceNumber}/${savedSilencedAlert.ircs}/${savedSilencedAlert.externalReferenceNumber}.",
)

return savedSilencedAlert
}

private fun getSilencedAlertWithNatinf(silencedAlert: SilencedAlert): SilencedAlert {
val nextValueWithNatinf: AlertType = when (silencedAlert.value.type) {
AlertTypeMapping.THREE_MILES_TRAWLING_ALERT -> ThreeMilesTrawlingAlert()
AlertTypeMapping.FRENCH_EEZ_FISHING_ALERT -> FrenchEEZFishingAlert()
AlertTypeMapping.TWELVE_MILES_FISHING_ALERT -> TwelveMilesFishingAlert()
AlertTypeMapping.MISSING_FAR_ALERT -> MissingFARAlert()
AlertTypeMapping.MISSING_FAR_48_HOURS_ALERT -> MissingFAR48HoursAlert()
else -> {
throw IllegalArgumentException("The alert type '${silencedAlert.value.type}' could not be silenced.")
}
}
return silencedAlert.copy(value = nextValueWithNatinf)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import org.slf4j.LoggerFactory
import java.time.ZonedDateTime

@UseCase
class SilenceOperationalAlert(
class SilencePendingAlert(
private val pendingAlertRepository: PendingAlertRepository,
private val silencedAlertRepository: SilencedAlertRepository,
private val lastPositionRepository: LastPositionRepository,
) {
private val logger = LoggerFactory.getLogger(SilenceOperationalAlert::class.java)
private val logger = LoggerFactory.getLogger(SilencePendingAlert::class.java)

fun execute(
alertId: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import org.slf4j.LoggerFactory
import java.time.ZonedDateTime

@UseCase
class ValidateOperationalAlert(
class ValidatePendingAlert(
private val pendingAlertRepository: PendingAlertRepository,
private val reportingRepository: ReportingRepository,
private val silencedAlertRepository: SilencedAlertRepository,
private val lastPositionRepository: LastPositionRepository,
) {
private val logger = LoggerFactory.getLogger(ValidateOperationalAlert::class.java)
private val logger = LoggerFactory.getLogger(ValidatePendingAlert::class.java)

fun execute(alertId: Int) {
val now = ZonedDateTime.now()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package fr.gouv.cnsp.monitorfish.infrastructure.api.bff

import com.fasterxml.jackson.databind.ObjectMapper
import fr.gouv.cnsp.monitorfish.domain.use_cases.alert.*
import fr.gouv.cnsp.monitorfish.infrastructure.api.input.SilenceOperationalAlertDataInput
import fr.gouv.cnsp.monitorfish.infrastructure.api.input.SilencedAlertDataInput
import fr.gouv.cnsp.monitorfish.infrastructure.api.outputs.PendingAlertDataOutput
import fr.gouv.cnsp.monitorfish.infrastructure.api.outputs.SilencedAlertDataOutput
import io.swagger.v3.oas.annotations.Operation
Expand All @@ -11,43 +13,45 @@ import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/bff/v1/operational_alerts")
@Tag(name = "APIs for Operational alerts")
class OperationalAlertController(
private val getOperationalAlerts: GetOperationalAlerts,
private val validateOperationalAlert: ValidateOperationalAlert,
private val silenceOperationalAlert: SilenceOperationalAlert,
@Tag(name = "APIs for pending Operational alerts")
class PendingAlertController(
private val getPendingAlerts: GetPendingAlerts,
private val validatePendingAlert: ValidatePendingAlert,
private val silencePendingAlert: SilencePendingAlert,
private val getSilencedAlerts: GetSilencedAlerts,
private val deleteSilencedOperationalAlert: DeleteSilencedOperationalAlert,
private val deleteSilencedAlert: DeleteSilencedAlert,
private val silenceAlert: SilenceAlert,
private val objectMapper: ObjectMapper,
) {

@GetMapping("")
@Operation(summary = "Get operational alerts")
@Operation(summary = "Get pending operational alerts")
fun getOperationalAlerts(): List<PendingAlertDataOutput> {
return getOperationalAlerts.execute().map {
return getPendingAlerts.execute().map {
PendingAlertDataOutput.fromPendingAlert(it)
}
}

@PutMapping(value = ["/{id}/validate"])
@Operation(summary = "Validate an operational alert")
@Operation(summary = "Validate a pending operational alert")
fun validateAlert(
@PathParam("Alert id")
@PathVariable(name = "id")
id: Int,
) {
return validateOperationalAlert.execute(id)
return validatePendingAlert.execute(id)
}

@PutMapping(value = ["/{id}/silence"], consumes = ["application/json"])
@Operation(summary = "Silence an operational alert")
@Operation(summary = "Silence a pending operational alert")
fun silenceAlert(
@PathParam("Alert id")
@PathVariable(name = "id")
id: Int,
@RequestBody
silenceOperationalAlertData: SilenceOperationalAlertDataInput,
): SilencedAlertDataOutput {
val silencedAlert = silenceOperationalAlert.execute(
val silencedAlert = silencePendingAlert.execute(
id,
silenceOperationalAlertData.silencedAlertPeriod,
silenceOperationalAlertData.beforeDateTime,
Expand All @@ -71,6 +75,17 @@ class OperationalAlertController(
@PathVariable(name = "id")
id: Int,
) {
return deleteSilencedOperationalAlert.execute(id)
return deleteSilencedAlert.execute(id)
}

@PostMapping(value = ["/silenced"], consumes = ["application/json"])
@Operation(summary = "Silence an operational alert")
fun silenceAlert(
@RequestBody
silenceAlertData: SilencedAlertDataInput,
): SilencedAlertDataOutput {
val silencedAlert = silenceAlert.execute(silenceAlertData.toSilencedAlert(objectMapper))

return SilencedAlertDataOutput.fromSilencedAlert(silencedAlert)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package fr.gouv.cnsp.monitorfish.infrastructure.api.input

import com.fasterxml.jackson.databind.ObjectMapper
import com.neovisionaries.i18n.CountryCode
import fr.gouv.cnsp.monitorfish.domain.entities.alerts.SilencedAlert
import fr.gouv.cnsp.monitorfish.domain.entities.alerts.type.AlertType
import fr.gouv.cnsp.monitorfish.domain.entities.vessel.VesselIdentifier
import java.time.ZonedDateTime

class SilencedAlertDataInput(
val vesselId: Int? = null,
val vesselName: String? = null,
val internalReferenceNumber: String? = null,
val externalReferenceNumber: String? = null,
val ircs: String? = null,
val vesselIdentifier: VesselIdentifier,
val flagState: CountryCode,
val silencedBeforeDate: ZonedDateTime,
val value: String,
) {
fun toSilencedAlert(objectMapper: ObjectMapper) = SilencedAlert(
vesselId = this.vesselId,
vesselName = this.vesselName,
internalReferenceNumber = this.internalReferenceNumber,
externalReferenceNumber = this.externalReferenceNumber,
ircs = this.ircs,
vesselIdentifier = this.vesselIdentifier,
flagState = this.flagState,
silencedBeforeDate = this.silencedBeforeDate,
value = objectMapper.readValue(value, AlertType::class.java),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import java.time.ZonedDateTime

class SilencedAlertDataOutput(
val id: Int? = null,
val vesselId: Int? = null,
val vesselName: String? = null,
val internalReferenceNumber: String? = null,
val externalReferenceNumber: String? = null,
Expand All @@ -20,6 +21,7 @@ class SilencedAlertDataOutput(
companion object {
fun fromSilencedAlert(silencedAlert: SilencedAlert) = SilencedAlertDataOutput(
id = silencedAlert.id,
vesselId = silencedAlert.vesselId,
vesselName = silencedAlert.vesselName,
internalReferenceNumber = silencedAlert.internalReferenceNumber,
externalReferenceNumber = silencedAlert.externalReferenceNumber,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package fr.gouv.cnsp.monitorfish.infrastructure.api.public_api

import fr.gouv.cnsp.monitorfish.domain.use_cases.alert.ValidatePendingAlert
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import jakarta.websocket.server.PathParam
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping("/api/v1/operational_alerts")
@Tag(name = "Public APIs for Operational alerts")
class PublicOperationalAlertController(
private val validatePendingAlert: ValidatePendingAlert,
) {

@PutMapping(value = ["/{id}/validate"])
@Operation(summary = "Validate an operational alert")
fun validateAlert(
@PathParam("Alert id")
@PathVariable(name = "id")
id: Int,
) {
return validatePendingAlert.execute(id)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ data class SilencedAlertEntity(
@Basic(optional = false)
@Column(name = "id", unique = true)
val id: Int? = null,
@Column(name = "vessel_id")
val vesselId: Int? = null,
@Column(name = "vessel_name")
val vesselName: String? = null,
@Column(name = "internal_reference_number")
Expand Down Expand Up @@ -55,6 +57,7 @@ data class SilencedAlertEntity(
flagState = flagState,
silencedBeforeDate = silencedBeforeDate,
value = mapper.readValue(value, AlertType::class.java),
vesselId = vesselId,
wasValidated = wasValidated,
)
}
Expand All @@ -74,7 +77,24 @@ data class SilencedAlertEntity(
flagState = alert.flagState,
silencedBeforeDate = silencedBeforeDate,
value = mapper.writeValueAsString(alert.value),
vesselId = alert.vesselId,
wasValidated = isValidated,
)

fun fromSilencedAlert(
mapper: ObjectMapper,
silencedAlert: SilencedAlert,
) = SilencedAlertEntity(
vesselName = silencedAlert.vesselName,
internalReferenceNumber = silencedAlert.internalReferenceNumber,
externalReferenceNumber = silencedAlert.externalReferenceNumber,
ircs = silencedAlert.ircs,
vesselIdentifier = silencedAlert.vesselIdentifier,
flagState = silencedAlert.flagState,
silencedBeforeDate = silencedAlert.silencedBeforeDate,
value = mapper.writeValueAsString(silencedAlert.value),
vesselId = silencedAlert.vesselId,
wasValidated = silencedAlert.wasValidated,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ class JpaSilencedAlertRepository(
).toSilencedAlert(mapper)
}

override fun save(
silencedAlert: SilencedAlert,
): SilencedAlert {
return dbSilencedAlertRepository.save(
SilencedAlertEntity.fromSilencedAlert(mapper, silencedAlert),
).toSilencedAlert(mapper)
}

override fun findAllCurrentSilencedAlerts(): List<SilencedAlert> {
val now = ZonedDateTime.now()
return dbSilencedAlertRepository.findAllBySilencedBeforeDateAfter(now).map { it.toSilencedAlert(mapper) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE public.silenced_alerts
ADD COLUMN vessel_id integer;
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,10 @@ VALUES ('PHENOMENE', 'FAK000999999', 'DONTSINK', 'CALLME','FR', NOW(), '9463715
'"seaFront": "NAMO",' ||
'"riskFactor": 3.4,' ||
'"type": "MISSING_FAR_ALERT"' ||
'}')::jsonb, -12.569, 8.851);
'}')::jsonb, -12.569, 8.851),
('MAINTENANT RÉPONSE ANNÉE', 'ABC000823773', 'HG384751', 'NK2932',
'FR', (NOW() AT TIME ZONE 'UTC')::TIMESTAMP - interval '2 hours 17 minutes', null, 'INTERNAL_REFERENCE_NUMBER', ('{' ||
'"seaFront": "NAMO",' ||
'"riskFactor": 1.5,' ||
'"type": "MISSING_FAR_48_HOURS_ALERT"' ||
'}')::jsonb, -10.569, 48.851);
Loading

0 comments on commit 377b7ed

Please sign in to comment.