From bd2be7e09aa0635f28dd61742269d4d05c12f85c Mon Sep 17 00:00:00 2001 From: aleckvincent Date: Tue, 13 Feb 2024 14:31:39 +0100 Subject: [PATCH 01/19] [WIP] export odt depuis Rpn 1 --- .../mission/IRpnExportRepository.kt | 25 ++++++++ .../domain/use_cases/mission/ExportMission.kt | 61 +++++++++++++++++++ .../api/adapters/inputs/RpnExportOdtOutput.kt | 21 +++++++ .../infrastructure/bff/ExportController.kt | 26 ++++++++ .../mission/APIRpnExportRepository.kt | 56 +++++++++++++++++ .../main/resources/graphql/export.graphqls | 7 +++ 6 files changed, 196 insertions(+) create mode 100644 backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt create mode 100644 backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt create mode 100644 backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/adapters/inputs/RpnExportOdtOutput.kt create mode 100644 backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ExportController.kt create mode 100644 backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/mission/APIRpnExportRepository.kt create mode 100644 backend/src/main/resources/graphql/export.graphqls diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt new file mode 100644 index 000000000..a95a93a0b --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt @@ -0,0 +1,25 @@ +package fr.gouv.dgampa.rapportnav.domain.repositories.mission + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity + +interface IRpnExportRepository { + + fun exportOdt( + service: String, + id: String, + startDateTime: String, + endDateTime: String, + presenceMer: Map, + presenceQuai: Map, + indisponibilite: Map, + nbJoursMer: Int, + dureeMission: Int, + patrouilleEnv: Int, + patrouilleMigrant: Int, + distanceMilles: Int, + goMarine: Int, + essence: Int, + crew: List + + ) +} diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt new file mode 100644 index 000000000..8874b4bd2 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt @@ -0,0 +1,61 @@ +package fr.gouv.dgampa.rapportnav.domain.use_cases.mission + +import fr.gouv.dgampa.rapportnav.config.UseCase +import fr.gouv.dgampa.rapportnav.domain.repositories.mission.IRpnExportRepository +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.crew.GetAgentsCrewByMissionId + +@UseCase +class ExportMission( + private val exportRepository: IRpnExportRepository, + private val agentsCrewByMissionId: GetAgentsCrewByMissionId +) +{ + + fun exportOdt( + missionId: Int + ) + { + val presenceMer = mapOf( + "navigationEffective" to 25, + "mouillage" to 201, + "total" to 226 + ) + + val presenceQuai = mapOf( + "maintenance" to 40, + "meteo" to 5, + "representation" to 7, + "adminFormation" to 4, + "autre" to 8, + "contrPol" to 9, + "total" to 25 + ) + + val indisponibilite = mapOf( + "technique" to 25, + "personnel" to 201, + "total" to 78 + ) + + + val agentsCrew = agentsCrewByMissionId.execute(missionId = 2) + println("JE SUIS AU NIVEAU 2") + exportRepository.exportOdt( + service = "pam-iris-a", + id = "NAMO-2024-4", + startDateTime = "2024-02-12T12:34:56", + endDateTime = "2024-02-12T12:34:56", + presenceMer = presenceMer, + presenceQuai = presenceQuai, + indisponibilite = indisponibilite, + nbJoursMer = 4, + dureeMission = 3, + patrouilleEnv = 2, + patrouilleMigrant = 4, + distanceMilles = 4, + goMarine = 6, + essence = 3, + crew = listOf() + ) + } +} diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/adapters/inputs/RpnExportOdtOutput.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/adapters/inputs/RpnExportOdtOutput.kt new file mode 100644 index 000000000..e1a52ed5b --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/adapters/inputs/RpnExportOdtOutput.kt @@ -0,0 +1,21 @@ +package fr.gouv.dgampa.rapportnav.infrastructure.api.adapters.inputs + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity + +class RpnExportOdtOutput ( + val service: String, + val id: String, + val startDateTime: String, + val endDateTime: String, + val presenceMer: Map, + val presenceQuai: Map, + val indisponibilite: Map, + val nbJoursMer: Int, + val dureeMission: Int, + val patrouilleEnv: Int, + val patrouilleMigrant: Int, + val distanceMilles: Int, + val goMarine: Int, + val essence: Int, + val crew: List +) diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ExportController.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ExportController.kt new file mode 100644 index 000000000..4c883e0b6 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ExportController.kt @@ -0,0 +1,26 @@ +package fr.gouv.dgampa.rapportnav.infrastructure.bff + +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.ExportMission +import org.slf4j.LoggerFactory +import org.springframework.graphql.data.method.annotation.Argument +import org.springframework.graphql.data.method.annotation.QueryMapping +import org.springframework.stereotype.Controller + +@Controller +class ExportController( + private val exportMission: ExportMission +) { + + private val logger = LoggerFactory.getLogger(MissionController::class.java) + + @QueryMapping + fun export(@Argument missionId: Int): Unit { + try { + exportMission.exportOdt(missionId) + } + catch (e: Exception) { + logger.error("MissionController - failed to load missions from MonitorEnv", e) + throw Exception(e) + } + } +} diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/mission/APIRpnExportRepository.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/mission/APIRpnExportRepository.kt new file mode 100644 index 000000000..820c34dcc --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/mission/APIRpnExportRepository.kt @@ -0,0 +1,56 @@ +package fr.gouv.dgampa.rapportnav.infrastructure.database.repositories.mission + +import com.fasterxml.jackson.databind.ObjectMapper +import com.google.gson.Gson +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity +import fr.gouv.dgampa.rapportnav.domain.repositories.mission.IRpnExportRepository +import fr.gouv.dgampa.rapportnav.infrastructure.api.adapters.inputs.RpnExportOdtOutput +import org.springframework.stereotype.Repository +import java.net.URI +import java.net.http.HttpClient +import java.net.http.HttpRequest +import java.net.http.HttpRequest.BodyPublishers +import java.net.http.HttpResponse.BodyHandlers + +@Repository +class APIRpnExportRepository( + private val mapper: ObjectMapper +): IRpnExportRepository { + override fun exportOdt( + service: String, + id: String, + startDateTime: String, + endDateTime: String, + presenceMer: Map, + presenceQuai: Map, + indisponibilite: Map, + nbJoursMer: Int, + dureeMission: Int, + patrouilleEnv: Int, + patrouilleMigrant: Int, + distance: Int, + goMarine: Int, + essence: Int, + crew: List + ) { + println("JE SUIS DANS LE REPOSITORY") + val url = "http://127.0.0.1:8000/public_api/export/odt" + val client = HttpClient.newHttpClient() + + val content = RpnExportOdtOutput( + service, id, startDateTime, endDateTime, presenceMer, presenceQuai, indisponibilite, nbJoursMer, dureeMission, patrouilleEnv, patrouilleMigrant, distance, goMarine, essence, crew + ) + + val gson = Gson(); + + val json = gson.toJson(content) + + val request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("Content-Type", "application/json") + .POST(BodyPublishers.ofString(json)) + .build() + + val response = client.send(request, BodyHandlers.ofString()) + } +} diff --git a/backend/src/main/resources/graphql/export.graphqls b/backend/src/main/resources/graphql/export.graphqls new file mode 100644 index 000000000..445896854 --- /dev/null +++ b/backend/src/main/resources/graphql/export.graphqls @@ -0,0 +1,7 @@ +extend type Query { + export(missionId: ID): Void +} + +enum Void { + NO_RESULT +} From 31bd336dfbf89847e0be7a1482d942dff6aa2cbd Mon Sep 17 00:00:00 2001 From: aleckvincent Date: Thu, 22 Feb 2024 14:27:27 +0100 Subject: [PATCH 02/19] [export] ajout fuel, agentsCrew, mission dans export --- .../mission/IRpnExportRepository.kt | 12 ++-- .../domain/use_cases/mission/ExportMission.kt | 71 ++++++++++++------- .../infrastructure/bff/ExportController.kt | 2 +- .../APIRpnExportRepository.kt | 34 ++++++--- .../adapters/outputs}/RpnExportOdtOutput.kt | 16 ++--- 5 files changed, 85 insertions(+), 50 deletions(-) rename backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/{database/repositories/mission => rapportnav1}/APIRpnExportRepository.kt (67%) rename backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/{api/adapters/inputs => rapportnav1/adapters/outputs}/RpnExportOdtOutput.kt (58%) diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt index a95a93a0b..2630639e6 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt @@ -5,10 +5,10 @@ import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEnt interface IRpnExportRepository { fun exportOdt( - service: String, + service: String?, id: String, - startDateTime: String, - endDateTime: String, + startDateTime: String?, + endDateTime: String?, presenceMer: Map, presenceQuai: Map, indisponibilite: Map, @@ -16,9 +16,9 @@ interface IRpnExportRepository { dureeMission: Int, patrouilleEnv: Int, patrouilleMigrant: Int, - distanceMilles: Int, - goMarine: Int, - essence: Int, + distanceMilles: Float?, + goMarine: Float?, + essence: Float?, crew: List ) diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt index 8874b4bd2..a045e2d9c 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt @@ -1,20 +1,42 @@ package fr.gouv.dgampa.rapportnav.domain.use_cases.mission import fr.gouv.dgampa.rapportnav.config.UseCase +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.generalInfo.MissionGeneralInfoEntity import fr.gouv.dgampa.rapportnav.domain.repositories.mission.IRpnExportRepository import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.crew.GetAgentsCrewByMissionId +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.generalInfo.GetMissionGeneralInfoByMissionId +import org.slf4j.LoggerFactory @UseCase class ExportMission( private val exportRepository: IRpnExportRepository, - private val agentsCrewByMissionId: GetAgentsCrewByMissionId + private val getMissionGeneralInfoByMissionId: GetMissionGeneralInfoByMissionId, + private val agentsCrewByMissionId: GetAgentsCrewByMissionId, + private val getEnvMissionById: GetEnvMissionById, + private val getNavMissionById: GetNavMissionById, + private val getFishActionsByMissionId: GetFishActionsByMissionId ) { + private val logger = LoggerFactory.getLogger(ExportMission::class.java) + + fun exportOdt(missionId: Int) { + + val generalInfo: MissionGeneralInfoEntity? = getMissionGeneralInfoByMissionId.execute(missionId) + val agentsCrew: List = agentsCrewByMissionId.execute(missionId = missionId) + + /* val envMission = getEnvMissionById.execute(missionId = missionId) + val fishMissionActions = getFishActionsByMissionId.execute(missionId = missionId) + val navMission = getNavMissionById.execute(missionId = missionId) + + if (envMission === null) { + logger.error("MissionExport - failed to load mission from MonitorEnv") + return; + } + + val mission: Mission = Mission.fromMissionEntity(MissionEntity(envMission, navMission, fishMissionActions))*/ + - fun exportOdt( - missionId: Int - ) - { val presenceMer = mapOf( "navigationEffective" to 25, "mouillage" to 201, @@ -38,24 +60,25 @@ class ExportMission( ) - val agentsCrew = agentsCrewByMissionId.execute(missionId = 2) - println("JE SUIS AU NIVEAU 2") - exportRepository.exportOdt( - service = "pam-iris-a", - id = "NAMO-2024-4", - startDateTime = "2024-02-12T12:34:56", - endDateTime = "2024-02-12T12:34:56", - presenceMer = presenceMer, - presenceQuai = presenceQuai, - indisponibilite = indisponibilite, - nbJoursMer = 4, - dureeMission = 3, - patrouilleEnv = 2, - patrouilleMigrant = 4, - distanceMilles = 4, - goMarine = 6, - essence = 3, - crew = listOf() - ) + + if (generalInfo != null) { + exportRepository.exportOdt( + service = "pam-iris-a", + id = "NAMO-2024-4", + startDateTime = "2024-02-12T12:34:56", + endDateTime = "2024-02-12T12:34:56", + presenceMer = presenceMer, + presenceQuai = presenceQuai, + indisponibilite = indisponibilite, + nbJoursMer = 4, + dureeMission = 3, + patrouilleEnv = 2, + patrouilleMigrant = 4, + distanceMilles = generalInfo.distanceInNauticalMiles, + goMarine = generalInfo.consumedGOInLiters, + essence = generalInfo.consumedFuelInLiters, + crew = agentsCrew, + ) + } } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ExportController.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ExportController.kt index 4c883e0b6..d56e64162 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ExportController.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ExportController.kt @@ -19,7 +19,7 @@ class ExportController( exportMission.exportOdt(missionId) } catch (e: Exception) { - logger.error("MissionController - failed to load missions from MonitorEnv", e) + logger.error("MissionController - failed to load mission from RapportNav 1", e) throw Exception(e) } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/mission/APIRpnExportRepository.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/APIRpnExportRepository.kt similarity index 67% rename from backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/mission/APIRpnExportRepository.kt rename to backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/APIRpnExportRepository.kt index 820c34dcc..e7c8d8250 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/mission/APIRpnExportRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/APIRpnExportRepository.kt @@ -1,10 +1,10 @@ -package fr.gouv.dgampa.rapportnav.infrastructure.database.repositories.mission +package fr.gouv.dgampa.rapportnav.infrastructure.rapportnav1 import com.fasterxml.jackson.databind.ObjectMapper import com.google.gson.Gson import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity import fr.gouv.dgampa.rapportnav.domain.repositories.mission.IRpnExportRepository -import fr.gouv.dgampa.rapportnav.infrastructure.api.adapters.inputs.RpnExportOdtOutput +import fr.gouv.dgampa.rapportnav.infrastructure.rapportnav1.adapters.outputs.RpnExportOdtOutput import org.springframework.stereotype.Repository import java.net.URI import java.net.http.HttpClient @@ -17,10 +17,10 @@ class APIRpnExportRepository( private val mapper: ObjectMapper ): IRpnExportRepository { override fun exportOdt( - service: String, + service: String?, id: String, - startDateTime: String, - endDateTime: String, + startDateTime: String?, + endDateTime: String?, presenceMer: Map, presenceQuai: Map, indisponibilite: Map, @@ -28,21 +28,33 @@ class APIRpnExportRepository( dureeMission: Int, patrouilleEnv: Int, patrouilleMigrant: Int, - distance: Int, - goMarine: Int, - essence: Int, + distanceMilles: Float?, + goMarine: Float?, + essence: Float?, crew: List ) { - println("JE SUIS DANS LE REPOSITORY") val url = "http://127.0.0.1:8000/public_api/export/odt" val client = HttpClient.newHttpClient() val content = RpnExportOdtOutput( - service, id, startDateTime, endDateTime, presenceMer, presenceQuai, indisponibilite, nbJoursMer, dureeMission, patrouilleEnv, patrouilleMigrant, distance, goMarine, essence, crew + service, + id, + startDateTime, + endDateTime, + presenceMer, + presenceQuai, + indisponibilite, + nbJoursMer, + dureeMission, + patrouilleEnv, + patrouilleMigrant, + distanceMilles, + goMarine, + essence, + crew ) val gson = Gson(); - val json = gson.toJson(content) val request = HttpRequest.newBuilder() diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/adapters/inputs/RpnExportOdtOutput.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/adapters/outputs/RpnExportOdtOutput.kt similarity index 58% rename from backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/adapters/inputs/RpnExportOdtOutput.kt rename to backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/adapters/outputs/RpnExportOdtOutput.kt index e1a52ed5b..97fabf365 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/adapters/inputs/RpnExportOdtOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/adapters/outputs/RpnExportOdtOutput.kt @@ -1,12 +1,12 @@ -package fr.gouv.dgampa.rapportnav.infrastructure.api.adapters.inputs +package fr.gouv.dgampa.rapportnav.infrastructure.rapportnav1.adapters.outputs import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity class RpnExportOdtOutput ( - val service: String, - val id: String, - val startDateTime: String, - val endDateTime: String, + val service: String?, + val id: String?, + val startDateTime: String?, + val endDateTime: String?, val presenceMer: Map, val presenceQuai: Map, val indisponibilite: Map, @@ -14,8 +14,8 @@ class RpnExportOdtOutput ( val dureeMission: Int, val patrouilleEnv: Int, val patrouilleMigrant: Int, - val distanceMilles: Int, - val goMarine: Int, - val essence: Int, + val distanceMilles: Float?, + val goMarine: Float?, + val essence: Float?, val crew: List ) From 212b24e4b44af0e4ba69753bfa0634b52fa390be Mon Sep 17 00:00:00 2001 From: aleckvincent Date: Tue, 13 Feb 2024 14:31:39 +0100 Subject: [PATCH 03/19] [WIP] Notes libres backend --- .../api/adapters/inputs/RpnExportOdtOutput.kt | 21 +++++++ .../infrastructure/bff/ExportController.kt | 2 +- .../mission/APIRpnExportRepository.kt | 56 +++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/adapters/inputs/RpnExportOdtOutput.kt create mode 100644 backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/mission/APIRpnExportRepository.kt diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/adapters/inputs/RpnExportOdtOutput.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/adapters/inputs/RpnExportOdtOutput.kt new file mode 100644 index 000000000..e1a52ed5b --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/adapters/inputs/RpnExportOdtOutput.kt @@ -0,0 +1,21 @@ +package fr.gouv.dgampa.rapportnav.infrastructure.api.adapters.inputs + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity + +class RpnExportOdtOutput ( + val service: String, + val id: String, + val startDateTime: String, + val endDateTime: String, + val presenceMer: Map, + val presenceQuai: Map, + val indisponibilite: Map, + val nbJoursMer: Int, + val dureeMission: Int, + val patrouilleEnv: Int, + val patrouilleMigrant: Int, + val distanceMilles: Int, + val goMarine: Int, + val essence: Int, + val crew: List +) diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ExportController.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ExportController.kt index d56e64162..4c883e0b6 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ExportController.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ExportController.kt @@ -19,7 +19,7 @@ class ExportController( exportMission.exportOdt(missionId) } catch (e: Exception) { - logger.error("MissionController - failed to load mission from RapportNav 1", e) + logger.error("MissionController - failed to load missions from MonitorEnv", e) throw Exception(e) } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/mission/APIRpnExportRepository.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/mission/APIRpnExportRepository.kt new file mode 100644 index 000000000..820c34dcc --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/mission/APIRpnExportRepository.kt @@ -0,0 +1,56 @@ +package fr.gouv.dgampa.rapportnav.infrastructure.database.repositories.mission + +import com.fasterxml.jackson.databind.ObjectMapper +import com.google.gson.Gson +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity +import fr.gouv.dgampa.rapportnav.domain.repositories.mission.IRpnExportRepository +import fr.gouv.dgampa.rapportnav.infrastructure.api.adapters.inputs.RpnExportOdtOutput +import org.springframework.stereotype.Repository +import java.net.URI +import java.net.http.HttpClient +import java.net.http.HttpRequest +import java.net.http.HttpRequest.BodyPublishers +import java.net.http.HttpResponse.BodyHandlers + +@Repository +class APIRpnExportRepository( + private val mapper: ObjectMapper +): IRpnExportRepository { + override fun exportOdt( + service: String, + id: String, + startDateTime: String, + endDateTime: String, + presenceMer: Map, + presenceQuai: Map, + indisponibilite: Map, + nbJoursMer: Int, + dureeMission: Int, + patrouilleEnv: Int, + patrouilleMigrant: Int, + distance: Int, + goMarine: Int, + essence: Int, + crew: List + ) { + println("JE SUIS DANS LE REPOSITORY") + val url = "http://127.0.0.1:8000/public_api/export/odt" + val client = HttpClient.newHttpClient() + + val content = RpnExportOdtOutput( + service, id, startDateTime, endDateTime, presenceMer, presenceQuai, indisponibilite, nbJoursMer, dureeMission, patrouilleEnv, patrouilleMigrant, distance, goMarine, essence, crew + ) + + val gson = Gson(); + + val json = gson.toJson(content) + + val request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("Content-Type", "application/json") + .POST(BodyPublishers.ofString(json)) + .build() + + val response = client.send(request, BodyHandlers.ofString()) + } +} From 3bf6092f708e5de675bedd3e060fbe5ae5827732 Mon Sep 17 00:00:00 2001 From: lwih Date: Tue, 20 Feb 2024 12:34:32 +0100 Subject: [PATCH 04/19] Finish notes libres --- .../api/adapters/inputs/RpnExportOdtOutput.kt | 21 ------- .../infrastructure/bff/ExportController.kt | 26 --------- .../mission/APIRpnExportRepository.kt | 56 ------------------- .../main/resources/graphql/export.graphqls | 7 --- 4 files changed, 110 deletions(-) delete mode 100644 backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/adapters/inputs/RpnExportOdtOutput.kt delete mode 100644 backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ExportController.kt delete mode 100644 backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/mission/APIRpnExportRepository.kt delete mode 100644 backend/src/main/resources/graphql/export.graphqls diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/adapters/inputs/RpnExportOdtOutput.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/adapters/inputs/RpnExportOdtOutput.kt deleted file mode 100644 index e1a52ed5b..000000000 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/api/adapters/inputs/RpnExportOdtOutput.kt +++ /dev/null @@ -1,21 +0,0 @@ -package fr.gouv.dgampa.rapportnav.infrastructure.api.adapters.inputs - -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity - -class RpnExportOdtOutput ( - val service: String, - val id: String, - val startDateTime: String, - val endDateTime: String, - val presenceMer: Map, - val presenceQuai: Map, - val indisponibilite: Map, - val nbJoursMer: Int, - val dureeMission: Int, - val patrouilleEnv: Int, - val patrouilleMigrant: Int, - val distanceMilles: Int, - val goMarine: Int, - val essence: Int, - val crew: List -) diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ExportController.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ExportController.kt deleted file mode 100644 index 4c883e0b6..000000000 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/ExportController.kt +++ /dev/null @@ -1,26 +0,0 @@ -package fr.gouv.dgampa.rapportnav.infrastructure.bff - -import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.ExportMission -import org.slf4j.LoggerFactory -import org.springframework.graphql.data.method.annotation.Argument -import org.springframework.graphql.data.method.annotation.QueryMapping -import org.springframework.stereotype.Controller - -@Controller -class ExportController( - private val exportMission: ExportMission -) { - - private val logger = LoggerFactory.getLogger(MissionController::class.java) - - @QueryMapping - fun export(@Argument missionId: Int): Unit { - try { - exportMission.exportOdt(missionId) - } - catch (e: Exception) { - logger.error("MissionController - failed to load missions from MonitorEnv", e) - throw Exception(e) - } - } -} diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/mission/APIRpnExportRepository.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/mission/APIRpnExportRepository.kt deleted file mode 100644 index 820c34dcc..000000000 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/database/repositories/mission/APIRpnExportRepository.kt +++ /dev/null @@ -1,56 +0,0 @@ -package fr.gouv.dgampa.rapportnav.infrastructure.database.repositories.mission - -import com.fasterxml.jackson.databind.ObjectMapper -import com.google.gson.Gson -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity -import fr.gouv.dgampa.rapportnav.domain.repositories.mission.IRpnExportRepository -import fr.gouv.dgampa.rapportnav.infrastructure.api.adapters.inputs.RpnExportOdtOutput -import org.springframework.stereotype.Repository -import java.net.URI -import java.net.http.HttpClient -import java.net.http.HttpRequest -import java.net.http.HttpRequest.BodyPublishers -import java.net.http.HttpResponse.BodyHandlers - -@Repository -class APIRpnExportRepository( - private val mapper: ObjectMapper -): IRpnExportRepository { - override fun exportOdt( - service: String, - id: String, - startDateTime: String, - endDateTime: String, - presenceMer: Map, - presenceQuai: Map, - indisponibilite: Map, - nbJoursMer: Int, - dureeMission: Int, - patrouilleEnv: Int, - patrouilleMigrant: Int, - distance: Int, - goMarine: Int, - essence: Int, - crew: List - ) { - println("JE SUIS DANS LE REPOSITORY") - val url = "http://127.0.0.1:8000/public_api/export/odt" - val client = HttpClient.newHttpClient() - - val content = RpnExportOdtOutput( - service, id, startDateTime, endDateTime, presenceMer, presenceQuai, indisponibilite, nbJoursMer, dureeMission, patrouilleEnv, patrouilleMigrant, distance, goMarine, essence, crew - ) - - val gson = Gson(); - - val json = gson.toJson(content) - - val request = HttpRequest.newBuilder() - .uri(URI.create(url)) - .header("Content-Type", "application/json") - .POST(BodyPublishers.ofString(json)) - .build() - - val response = client.send(request, BodyHandlers.ofString()) - } -} diff --git a/backend/src/main/resources/graphql/export.graphqls b/backend/src/main/resources/graphql/export.graphqls deleted file mode 100644 index 445896854..000000000 --- a/backend/src/main/resources/graphql/export.graphqls +++ /dev/null @@ -1,7 +0,0 @@ -extend type Query { - export(missionId: ID): Void -} - -enum Void { - NO_RESULT -} From fdc5afa7aa67c0e7a23fbaf13e7b8925638501d7 Mon Sep 17 00:00:00 2001 From: aleckvincent Date: Thu, 22 Feb 2024 15:57:32 +0100 Subject: [PATCH 05/19] WIP export with statuses --- .../domain/use_cases/mission/ExportMission.kt | 4 ++ .../GetStatusDurationsThroughoutMission.kt | 50 +++++++++++++++++++ .../mission/status/ExportMissionTests.kt | 29 +++++++++++ 3 files changed, 83 insertions(+) create mode 100644 backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/status/GetStatusDurationsThroughoutMission.kt create mode 100644 backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/status/ExportMissionTests.kt diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt index a045e2d9c..bb797ff1c 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt @@ -1,12 +1,16 @@ package fr.gouv.dgampa.rapportnav.domain.use_cases.mission import fr.gouv.dgampa.rapportnav.config.UseCase +import fr.gouv.dgampa.rapportnav.domain.entities.mission.ExtendedEnvMissionEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.generalInfo.MissionGeneralInfoEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusType import fr.gouv.dgampa.rapportnav.domain.repositories.mission.IRpnExportRepository +import fr.gouv.dgampa.rapportnav.domain.repositories.mission.action.INavActionStatusRepository import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.crew.GetAgentsCrewByMissionId import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.generalInfo.GetMissionGeneralInfoByMissionId import org.slf4j.LoggerFactory +import java.time.Duration @UseCase class ExportMission( diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/status/GetStatusDurationsThroughoutMission.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/status/GetStatusDurationsThroughoutMission.kt new file mode 100644 index 000000000..d172cb4e5 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/status/GetStatusDurationsThroughoutMission.kt @@ -0,0 +1,50 @@ +package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.status + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity +import java.time.Duration +import java.time.ZonedDateTime + +class GetStatusDurationsThroughoutMission() { + fun execute(missionStartDateTime: ZonedDateTime, missionEndDateTime: ZonedDateTime, statuses: List?): List? { + return statuses?.let { + + + +// [ +// {status: ActionStatusType.NAVIGATING, value: 123, reason: ActionStatusReason.ADMINISTRATION }, +// {status: ActionStatusType.NAVIGATING, value: 123, reason: ActionStatusReason.METEO }, +// ] + + + + + statuses.mapIndexed { index, actionStatusEntity -> + { + var hours: Long? = null; + // 1. calcul duree + if (index === 0) { + // duree avec missionStartDateTime + val duration = Duration.between(missionStartDateTime, actionStatusEntity.startDateTimeUtc) + hours = duration.toHours() + val minutes = duration.toMinutes() % 60 + + if (minutes > 30) { + hours += 1 + } + } else if (index === statuses.size - 1) { + val duration = Duration.between(actionStatusEntity.startDateTimeUtc, missionEndDateTime) + hours = duration.toHours() + val minutes = duration.toMinutes() % 60 + } else { + // duree avec element precedent + val duration = Duration.between( + statuses.get(index - 1).startDateTimeUtc, + actionStatusEntity.startDateTimeUtc + ) + hours = duration.toHours() + } + } + } + } + } +} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/status/ExportMissionTests.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/status/ExportMissionTests.kt new file mode 100644 index 000000000..97a390cc2 --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/status/ExportMissionTests.kt @@ -0,0 +1,29 @@ +package fr.gouv.gmampa.rapportnav.domain.use_cases.mission.status + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.status.GetStatusDurationsThroughoutMission +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import java.time.ZoneId +import java.time.ZonedDateTime + + +@SpringBootTest(classes = [GetStatusDurationsThroughoutMission::class]) + +class ExportMissionTests { + + @Autowired + private lateinit var getStatusDurationsThroughoutMission: GetStatusDurationsThroughoutMission + + @Test + fun `execute should return XXX when no statuses`() { + val missionStartDateTime = ZonedDateTime.of(2023, 6, 19, 10, 0, 0, 0, ZoneId.of("Europe/Berlin")) + val missionEndDateTime = ZonedDateTime.of(2023, 6, 30, 10, 0, 0, 0, ZoneId.of("Europe/Berlin")) + val statuses = listOf() + + val values = getStatusDurationsThroughoutMission.execute(missionStartDateTime, missionEndDateTime, statuses); + assertThat(values).isEqualTo(null); + } +} From f3c7e9e2ed5f090533b61928832d41b9c3b9595c Mon Sep 17 00:00:00 2001 From: aleckvincent Date: Thu, 22 Feb 2024 15:57:45 +0100 Subject: [PATCH 06/19] Changes by louis --- .../domain/use_cases/mission/ExportMission.kt | 145 +++++++++++------- 1 file changed, 91 insertions(+), 54 deletions(-) diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt index bb797ff1c..3afa9c7a1 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt @@ -19,70 +19,107 @@ class ExportMission( private val agentsCrewByMissionId: GetAgentsCrewByMissionId, private val getEnvMissionById: GetEnvMissionById, private val getNavMissionById: GetNavMissionById, - private val getFishActionsByMissionId: GetFishActionsByMissionId + private val getFishActionsByMissionId: GetFishActionsByMissionId, + private val navActionStatus: INavActionStatusRepository ) { private val logger = LoggerFactory.getLogger(ExportMission::class.java) fun exportOdt(missionId: Int) { - val generalInfo: MissionGeneralInfoEntity? = getMissionGeneralInfoByMissionId.execute(missionId) - val agentsCrew: List = agentsCrewByMissionId.execute(missionId = missionId) + val mission: ExtendedEnvMissionEntity? = getEnvMissionById.execute(missionId = missionId) - /* val envMission = getEnvMissionById.execute(missionId = missionId) - val fishMissionActions = getFishActionsByMissionId.execute(missionId = missionId) - val navMission = getNavMissionById.execute(missionId = missionId) - - if (envMission === null) { - logger.error("MissionExport - failed to load mission from MonitorEnv") - return; + if (mission?.mission == null) { + return } + else { + val generalInfo: MissionGeneralInfoEntity? = getMissionGeneralInfoByMissionId.execute(missionId) + val agentsCrew: List = agentsCrewByMissionId.execute(missionId = missionId) + val statuses = navActionStatus.findAllByMissionId(missionId = missionId).sortedBy { it.startDateTimeUtc }.map { it.toActionStatusEntity() } + + val missionStartDateTime = mission.mission.startDateTimeUtc + val missionEndDateTime = mission.mission.endDateTimeUtc + val seaPresence: MutableMap = mutableMapOf() + + /* val envMission = getEnvMissionById.execute(missionId = missionId) + val fishMissionActions = getFishActionsByMissionId.execute(missionId = missionId) + val navMission = getNavMissionById.execute(missionId = missionId) + + if (envMission === null) { + logger.error("MissionExport - failed to load mission from MonitorEnv") + return; + } + + val mission: Mission = Mission.fromMissionEntity(MissionEntity(envMission, navMission, fishMissionActions))*/ + + statuses.mapIndexed { index, actionStatusEntity -> { + + + // 2. remplir tableau + var navigation: Long? = null; + var anchorage: Long? = null; + when (actionStatusEntity.status.name) { + ActionStatusType.NAVIGATING.toString() -> navigation = hours + ActionStatusType.ANCHORED.toString() -> anchorage = hours + else -> null + } + var totalSeaPresence: Long? = navigation?.plus(anchorage ?: 0L) + + + - val mission: Mission = Mission.fromMissionEntity(MissionEntity(envMission, navMission, fishMissionActions))*/ - - - val presenceMer = mapOf( - "navigationEffective" to 25, - "mouillage" to 201, - "total" to 226 - ) - - val presenceQuai = mapOf( - "maintenance" to 40, - "meteo" to 5, - "representation" to 7, - "adminFormation" to 4, - "autre" to 8, - "contrPol" to 9, - "total" to 25 - ) - - val indisponibilite = mapOf( - "technique" to 25, - "personnel" to 201, - "total" to 78 - ) - - - - if (generalInfo != null) { - exportRepository.exportOdt( - service = "pam-iris-a", - id = "NAMO-2024-4", - startDateTime = "2024-02-12T12:34:56", - endDateTime = "2024-02-12T12:34:56", - presenceMer = presenceMer, - presenceQuai = presenceQuai, - indisponibilite = indisponibilite, - nbJoursMer = 4, - dureeMission = 3, - patrouilleEnv = 2, - patrouilleMigrant = 4, - distanceMilles = generalInfo.distanceInNauticalMiles, - goMarine = generalInfo.consumedGOInLiters, - essence = generalInfo.consumedFuelInLiters, - crew = agentsCrew, + } } + +// [ +// {status: ActionStatusType.UNAVAILABLE, value: 123, reason: ActionStatusReason.ADMINISTRATION }, +// {status: ActionStatusType.UNAVAILABLE, value: 123, reason: ActionStatusReason.METEO }, +// {status: ActionStatusType.ANCHORED, value: 123, }, +// ] + + val presenceMer = mapOf( + "navigationEffective" to VARIABLE.find({status = nagivation, reason = ...}), + "mouillage" to VARIABLE.find({status = ActionStatusType.ANCHORED}), + "total" to 226 + ) + + val presenceQuai = mapOf( + "maintenance" to 40, + "meteo" to 5, + "representation" to 7, + "adminFormation" to 4, + "autre" to 8, + "contrPol" to 9, + "total" to 25 + ) + + val indisponibilite = mapOf( + "technique" to 25, + "personnel" to 201, + "total" to 78 ) + + + + if (generalInfo != null) { + exportRepository.exportOdt( + service = "pam-iris-a", + id = "NAMO-2024-4", + startDateTime = "2024-02-12T12:34:56", + endDateTime = "2024-02-12T12:34:56", + presenceMer = presenceMer, + presenceQuai = presenceQuai, + indisponibilite = indisponibilite, + nbJoursMer = 4, + dureeMission = 3, + patrouilleEnv = 2, + patrouilleMigrant = 4, + distanceMilles = generalInfo.distanceInNauticalMiles, + goMarine = generalInfo.consumedGOInLiters, + essence = generalInfo.consumedFuelInLiters, + crew = agentsCrew, + ) + } } + } } From b2c71babf474aa4cfdb20716d100732801c105a2 Mon Sep 17 00:00:00 2001 From: lwih Date: Mon, 26 Feb 2024 11:48:28 +0100 Subject: [PATCH 07/19] Backend - compute durations for each status/reason combination --- .../domain/use_cases/mission/ExportMission.kt | 16 ++- .../mission/status/GetStatusDurations.kt | 119 ++++++++++++++++ .../GetStatusDurationsThroughoutMission.kt | 50 ------- .../mission/status/ExportMissionTests.kt | 29 ---- .../mission/status/GetStatusDurationsTests.kt | 129 ++++++++++++++++++ 5 files changed, 260 insertions(+), 83 deletions(-) create mode 100644 backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/status/GetStatusDurations.kt delete mode 100644 backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/status/GetStatusDurationsThroughoutMission.kt delete mode 100644 backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/status/ExportMissionTests.kt create mode 100644 backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/status/GetStatusDurationsTests.kt diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt index 3afa9c7a1..3c4e76cdf 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt @@ -9,6 +9,7 @@ import fr.gouv.dgampa.rapportnav.domain.repositories.mission.IRpnExportRepositor import fr.gouv.dgampa.rapportnav.domain.repositories.mission.action.INavActionStatusRepository import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.crew.GetAgentsCrewByMissionId import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.generalInfo.GetMissionGeneralInfoByMissionId +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.status.GetStatusDurations import org.slf4j.LoggerFactory import java.time.Duration @@ -20,9 +21,9 @@ class ExportMission( private val getEnvMissionById: GetEnvMissionById, private val getNavMissionById: GetNavMissionById, private val getFishActionsByMissionId: GetFishActionsByMissionId, - private val navActionStatus: INavActionStatusRepository -) -{ + private val navActionStatus: INavActionStatusRepository, + private val getStatusDurations: GetStatusDurations, +) { private val logger = LoggerFactory.getLogger(ExportMission::class.java) fun exportOdt(missionId: Int) { @@ -35,12 +36,19 @@ class ExportMission( else { val generalInfo: MissionGeneralInfoEntity? = getMissionGeneralInfoByMissionId.execute(missionId) val agentsCrew: List = agentsCrewByMissionId.execute(missionId = missionId) - val statuses = navActionStatus.findAllByMissionId(missionId = missionId).sortedBy { it.startDateTimeUtc }.map { it.toActionStatusEntity() } + val statuses = navActionStatus.findAllByMissionId(missionId = missionId).sortedBy { it.startDateTimeUtc } + .map { it.toActionStatusEntity() } val missionStartDateTime = mission.mission.startDateTimeUtc val missionEndDateTime = mission.mission.endDateTimeUtc val seaPresence: MutableMap = mutableMapOf() + val durations = getStatusDurations.computeActionDurations( + missionStartDateTime = mission.mission.startDateTimeUtc, + missionEndDateTime = mission.mission.endDateTimeUtc, + actions = statuses, + ) + /* val envMission = getEnvMissionById.execute(missionId = missionId) val fishMissionActions = getFishActionsByMissionId.execute(missionId = missionId) val navMission = getNavMissionById.execute(missionId = missionId) diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/status/GetStatusDurations.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/status/GetStatusDurations.kt new file mode 100644 index 000000000..8ad32e591 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/status/GetStatusDurations.kt @@ -0,0 +1,119 @@ +package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.status + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusReason +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusType +import java.time.ZonedDateTime + +class GetStatusDurations() { + + data class ActionStatusWithDuration( + val status: ActionStatusType, + val value: Long, + val reason: ActionStatusReason? = null + ) + + /** + * Computes the durations of various action statuses along with their reasons, returning an exhaustive list. + * + * This function computes the durations for each action status, including different reasons, and returns a list + * containing all possible combinations of status and reason with their corresponding durations. If a mission end + * time is provided, the duration is calculated until that time; otherwise, the duration is calculated until the + * current time for all but the last action. If no action statuses are provided, the function returns default + * durations with zero values for all possible status and reason combinations. + * + * Example of default output: + * ``` + * [ + * ActionStatusWithDuration(status=ANCHORED, value=0, reason=null), + * ActionStatusWithDuration(status=DOCKED, value=0, reason=MAINTENANCE), + * ActionStatusWithDuration(status=DOCKED, value=0, reason=WEATHER), + * ActionStatusWithDuration(status=DOCKED, value=0, reason=REPRESENTATION), + * ActionStatusWithDuration(status=DOCKED, value=0, reason=ADMINISTRATION), + * ActionStatusWithDuration(status=DOCKED, value=0, reason=HARBOUR_CONTROL), + * ActionStatusWithDuration(status=DOCKED, value=0, reason=OTHER), + * ActionStatusWithDuration(status=NAVIGATING, value=0, reason=null), + * ActionStatusWithDuration(status=UNAVAILABLE, value=0, reason=TECHNICAL), + * ActionStatusWithDuration(status=UNAVAILABLE, value=0, reason=PERSONNEL), + * ActionStatusWithDuration(status=UNAVAILABLE, value=0, reason=OTHER) + * ] + * ``` + * + * @param missionStartDateTime The start time of the mission. + * @param missionEndDateTime The end time of the mission (optional). If provided, durations are calculated + * until this time; otherwise, durations are calculated until the current time for all but the last action. + * @param actions The list of action status entities (optional). If not provided, default durations with zero + * values for all possible status and reason combinations are returned. + * @return A list of ActionStatusWithDuration objects representing the computed durations for each action status. + */ + fun computeActionDurations( + missionStartDateTime: ZonedDateTime, + missionEndDateTime: ZonedDateTime? = null, + actions: List? = listOf() + ): List { + val durations = mutableListOf() + + + var previousActionTime = missionStartDateTime + if (!actions.isNullOrEmpty()) { + for ((index, action) in actions.withIndex()) { + val startTime = if (index == 0) missionStartDateTime else previousActionTime + val duration = if (missionEndDateTime != null || index < actions.size - 1) { + // If missionEndDateTime is provided or it's not the last action, calculate duration normally + val endTime = + if (index == actions.size - 1) missionEndDateTime else actions[index + 1].startDateTimeUtc + endTime?.toEpochSecond()?.minus(startTime.toEpochSecond()) ?: 0 + } else { + // If missionEndDateTime is null and it's the last action, exclude this duration + 0 + } + + durations.add( + ActionStatusWithDuration( + status = action.status, + value = duration, + reason = action.reason + ) + ) + + previousActionTime = action.startDateTimeUtc + } + } + + // Add default values for all combinations of status and reason + for (status in ActionStatusType.values()) { + if (status != ActionStatusType.UNKNOWN) { + val reasons = getSelectOptionsForType(status) ?: listOf(null) + for (reason in reasons) { + if (durations.none { it.status == status && it.reason == reason }) { + durations.add(ActionStatusWithDuration(status = status, value = 0, reason = reason)) + } + } + } + } + + return durations.sortedBy { it.status.toString() } + } + + fun getSelectOptionsForType(type: ActionStatusType): List? { + return when (type) { + ActionStatusType.DOCKED -> listOf( + ActionStatusReason.MAINTENANCE, + ActionStatusReason.WEATHER, + ActionStatusReason.REPRESENTATION, + ActionStatusReason.ADMINISTRATION, + ActionStatusReason.HARBOUR_CONTROL, + ActionStatusReason.OTHER + ) + + ActionStatusType.UNAVAILABLE -> listOf( + ActionStatusReason.TECHNICAL, + ActionStatusReason.PERSONNEL, + ActionStatusReason.OTHER + ) + + else -> null + } + } + +} diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/status/GetStatusDurationsThroughoutMission.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/status/GetStatusDurationsThroughoutMission.kt deleted file mode 100644 index d172cb4e5..000000000 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/status/GetStatusDurationsThroughoutMission.kt +++ /dev/null @@ -1,50 +0,0 @@ -package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.status - -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity -import java.time.Duration -import java.time.ZonedDateTime - -class GetStatusDurationsThroughoutMission() { - fun execute(missionStartDateTime: ZonedDateTime, missionEndDateTime: ZonedDateTime, statuses: List?): List? { - return statuses?.let { - - - -// [ -// {status: ActionStatusType.NAVIGATING, value: 123, reason: ActionStatusReason.ADMINISTRATION }, -// {status: ActionStatusType.NAVIGATING, value: 123, reason: ActionStatusReason.METEO }, -// ] - - - - - statuses.mapIndexed { index, actionStatusEntity -> - { - var hours: Long? = null; - // 1. calcul duree - if (index === 0) { - // duree avec missionStartDateTime - val duration = Duration.between(missionStartDateTime, actionStatusEntity.startDateTimeUtc) - hours = duration.toHours() - val minutes = duration.toMinutes() % 60 - - if (minutes > 30) { - hours += 1 - } - } else if (index === statuses.size - 1) { - val duration = Duration.between(actionStatusEntity.startDateTimeUtc, missionEndDateTime) - hours = duration.toHours() - val minutes = duration.toMinutes() % 60 - } else { - // duree avec element precedent - val duration = Duration.between( - statuses.get(index - 1).startDateTimeUtc, - actionStatusEntity.startDateTimeUtc - ) - hours = duration.toHours() - } - } - } - } - } -} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/status/ExportMissionTests.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/status/ExportMissionTests.kt deleted file mode 100644 index 97a390cc2..000000000 --- a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/status/ExportMissionTests.kt +++ /dev/null @@ -1,29 +0,0 @@ -package fr.gouv.gmampa.rapportnav.domain.use_cases.mission.status - -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity -import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.status.GetStatusDurationsThroughoutMission -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import java.time.ZoneId -import java.time.ZonedDateTime - - -@SpringBootTest(classes = [GetStatusDurationsThroughoutMission::class]) - -class ExportMissionTests { - - @Autowired - private lateinit var getStatusDurationsThroughoutMission: GetStatusDurationsThroughoutMission - - @Test - fun `execute should return XXX when no statuses`() { - val missionStartDateTime = ZonedDateTime.of(2023, 6, 19, 10, 0, 0, 0, ZoneId.of("Europe/Berlin")) - val missionEndDateTime = ZonedDateTime.of(2023, 6, 30, 10, 0, 0, 0, ZoneId.of("Europe/Berlin")) - val statuses = listOf() - - val values = getStatusDurationsThroughoutMission.execute(missionStartDateTime, missionEndDateTime, statuses); - assertThat(values).isEqualTo(null); - } -} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/status/GetStatusDurationsTests.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/status/GetStatusDurationsTests.kt new file mode 100644 index 000000000..a58e2e5fa --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/status/GetStatusDurationsTests.kt @@ -0,0 +1,129 @@ +package fr.gouv.gmampa.rapportnav.domain.use_cases.mission.status + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusReason +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusType +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.status.GetStatusDurations +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import java.time.Duration +import java.time.ZoneId +import java.time.ZonedDateTime +import java.util.* + + +@SpringBootTest(classes = [GetStatusDurations::class]) + +class GetStatusDurationsTests { + + @Autowired + private lateinit var getStatusDurations: GetStatusDurations + + private lateinit var defaultReturnValues: List + + private val missionStartDateTime = ZonedDateTime.of(2023, 6, 19, 10, 0, 0, 0, ZoneId.of("Europe/Berlin")) + private val missionEndDateTime = ZonedDateTime.of(2023, 6, 30, 10, 0, 0, 0, ZoneId.of("Europe/Berlin")) + + @BeforeEach + fun setUp() { + defaultReturnValues = ActionStatusType.values().filter { it != ActionStatusType.UNKNOWN }.flatMap { status -> + getStatusDurations.getSelectOptionsForType(status)?.map { reason -> + GetStatusDurations.ActionStatusWithDuration(status, 0, reason) + } ?: listOf(GetStatusDurations.ActionStatusWithDuration(status, 0)) + } + } + + @Test + fun `execute should return the default when statuses is null`() { + val statuses = null + val values = getStatusDurations.computeActionDurations( + missionStartDateTime, + missionEndDateTime, + statuses + ); + assertThat(values).containsExactlyInAnyOrder(*defaultReturnValues.toTypedArray()) + } + + @Test + fun `execute should return the default when statuses is empty`() { + val statuses = listOf() + val values = getStatusDurations.computeActionDurations( + missionStartDateTime, + missionEndDateTime, + statuses + ); + assertThat(values).containsExactlyInAnyOrder(*defaultReturnValues.toTypedArray()) + } + + @Test + fun `execute should return duration for single status`() { + val actionStatus = ActionStatusEntity( + id = UUID.randomUUID(), + missionId = 1, + startDateTimeUtc = missionStartDateTime, + status = ActionStatusType.NAVIGATING, + reason = ActionStatusReason.ADMINISTRATION, + observations = "Test observation" + ) + val statuses = listOf(actionStatus) + + val values = getStatusDurations.computeActionDurations( + missionStartDateTime, + missionEndDateTime, + statuses + ) + + assertThat(values).contains( + GetStatusDurations.ActionStatusWithDuration( + ActionStatusType.NAVIGATING, + Duration.between(missionStartDateTime, missionEndDateTime).toSeconds(), + ActionStatusReason.ADMINISTRATION + ) + ) + } + + @Test + fun `execute should return durations excluding last action when missionEndDateTime is null`() { + // Define the list of action status entities + val actions = listOf( + ActionStatusEntity(UUID.randomUUID(), 1, missionStartDateTime, ActionStatusType.NAVIGATING), + ActionStatusEntity(UUID.randomUUID(), 1, missionStartDateTime.plusHours(2), ActionStatusType.ANCHORED), + ActionStatusEntity( + UUID.randomUUID(), + 1, + missionStartDateTime.plusHours(5), + ActionStatusType.DOCKED, + ActionStatusReason.WEATHER + ), + ActionStatusEntity( + UUID.randomUUID(), + 1, + missionStartDateTime.plusHours(7), + ActionStatusType.UNAVAILABLE, + ActionStatusReason.TECHNICAL + ) + ) + + // Call the computeActionDurations function with missionEndDateTime as null + val values = getStatusDurations.computeActionDurations( + missionStartDateTime, + null, + actions + ) + + // Calculate expected durations excluding the last action + val expectedValues = listOf( + GetStatusDurations.ActionStatusWithDuration(ActionStatusType.NAVIGATING, 7200), + GetStatusDurations.ActionStatusWithDuration(ActionStatusType.ANCHORED, 18000), + GetStatusDurations.ActionStatusWithDuration(ActionStatusType.DOCKED, 18000, ActionStatusReason.WEATHER), + GetStatusDurations.ActionStatusWithDuration(ActionStatusType.UNAVAILABLE, 0, ActionStatusReason.TECHNICAL), + ) + + // Assert that the computed durations match the expected values + assertThat(values).containsAll(expectedValues) + } + +} From 6eee598e1cc4bf72d1b65ad456d297f9e9d52712 Mon Sep 17 00:00:00 2001 From: aleckvincent Date: Tue, 27 Feb 2024 14:35:38 +0100 Subject: [PATCH 08/19] ajout mapping status et reason pour export --- .../mission/IRpnExportRepository.kt | 11 +-- .../domain/use_cases/mission/ExportMission.kt | 73 ++++--------------- 2 files changed, 21 insertions(+), 63 deletions(-) diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt index 2630639e6..01431dfeb 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt @@ -1,17 +1,18 @@ package fr.gouv.dgampa.rapportnav.domain.repositories.mission import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity +import java.time.ZonedDateTime interface IRpnExportRepository { fun exportOdt( service: String?, id: String, - startDateTime: String?, - endDateTime: String?, - presenceMer: Map, - presenceQuai: Map, - indisponibilite: Map, + startDateTime: ZonedDateTime?, + endDateTime: ZonedDateTime?, + presenceMer: Map, + presenceQuai: Map, + indisponibilite: Map, nbJoursMer: Int, dureeMission: Int, patrouilleEnv: Int, diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt index 3c4e76cdf..121071e3d 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt @@ -4,14 +4,13 @@ import fr.gouv.dgampa.rapportnav.config.UseCase import fr.gouv.dgampa.rapportnav.domain.entities.mission.ExtendedEnvMissionEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.generalInfo.MissionGeneralInfoEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusReason import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusType import fr.gouv.dgampa.rapportnav.domain.repositories.mission.IRpnExportRepository import fr.gouv.dgampa.rapportnav.domain.repositories.mission.action.INavActionStatusRepository import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.crew.GetAgentsCrewByMissionId import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.generalInfo.GetMissionGeneralInfoByMissionId import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.status.GetStatusDurations -import org.slf4j.LoggerFactory -import java.time.Duration @UseCase class ExportMission( @@ -19,12 +18,9 @@ class ExportMission( private val getMissionGeneralInfoByMissionId: GetMissionGeneralInfoByMissionId, private val agentsCrewByMissionId: GetAgentsCrewByMissionId, private val getEnvMissionById: GetEnvMissionById, - private val getNavMissionById: GetNavMissionById, - private val getFishActionsByMissionId: GetFishActionsByMissionId, private val navActionStatus: INavActionStatusRepository, private val getStatusDurations: GetStatusDurations, ) { - private val logger = LoggerFactory.getLogger(ExportMission::class.java) fun exportOdt(missionId: Int) { @@ -39,70 +35,31 @@ class ExportMission( val statuses = navActionStatus.findAllByMissionId(missionId = missionId).sortedBy { it.startDateTimeUtc } .map { it.toActionStatusEntity() } - val missionStartDateTime = mission.mission.startDateTimeUtc - val missionEndDateTime = mission.mission.endDateTimeUtc - val seaPresence: MutableMap = mutableMapOf() - val durations = getStatusDurations.computeActionDurations( missionStartDateTime = mission.mission.startDateTimeUtc, missionEndDateTime = mission.mission.endDateTimeUtc, actions = statuses, ) - /* val envMission = getEnvMissionById.execute(missionId = missionId) - val fishMissionActions = getFishActionsByMissionId.execute(missionId = missionId) - val navMission = getNavMissionById.execute(missionId = missionId) - - if (envMission === null) { - logger.error("MissionExport - failed to load mission from MonitorEnv") - return; - } - - val mission: Mission = Mission.fromMissionEntity(MissionEntity(envMission, navMission, fishMissionActions))*/ - - statuses.mapIndexed { index, actionStatusEntity -> { - - - // 2. remplir tableau - var navigation: Long? = null; - var anchorage: Long? = null; - when (actionStatusEntity.status.name) { - ActionStatusType.NAVIGATING.toString() -> navigation = hours - ActionStatusType.ANCHORED.toString() -> anchorage = hours - else -> null - } - var totalSeaPresence: Long? = navigation?.plus(anchorage ?: 0L) - - - - - } } - -// [ -// {status: ActionStatusType.UNAVAILABLE, value: 123, reason: ActionStatusReason.ADMINISTRATION }, -// {status: ActionStatusType.UNAVAILABLE, value: 123, reason: ActionStatusReason.METEO }, -// {status: ActionStatusType.ANCHORED, value: 123, }, -// ] - val presenceMer = mapOf( - "navigationEffective" to VARIABLE.find({status = nagivation, reason = ...}), - "mouillage" to VARIABLE.find({status = ActionStatusType.ANCHORED}), + "navigationEffective" to (durations.find {it.status == ActionStatusType.NAVIGATING}?.value ?: 0), + "mouillage" to (durations.find { it.status === ActionStatusType.ANCHORED }?.value ?: 0), "total" to 226 ) val presenceQuai = mapOf( - "maintenance" to 40, - "meteo" to 5, - "representation" to 7, - "adminFormation" to 4, - "autre" to 8, - "contrPol" to 9, + "maintenance" to (durations.find { it.reason === ActionStatusReason.MAINTENANCE }?.value ?: 0), + "meteo" to (durations.find { it.reason === ActionStatusReason.WEATHER }?.value ?: 0), + "representation" to (durations.find { it.reason === ActionStatusReason.REPRESENTATION }?.value ?: 0), + "adminFormation" to (durations.find { it.reason === ActionStatusReason.ADMINISTRATION }?.value ?: 0), + "autre" to (durations.find { it.reason === ActionStatusReason.OTHER }?.value ?: 0), + "contrPol" to (durations.find { it.reason === ActionStatusReason.HARBOUR_CONTROL }?.value ?: 0), "total" to 25 ) val indisponibilite = mapOf( - "technique" to 25, - "personnel" to 201, + "technique" to (durations.find { it.reason === ActionStatusReason.TECHNICAL }?.value ?: 0), + "personnel" to (durations.find { it.reason === ActionStatusReason.PERSONNEL }?.value ?: 0), "total" to 78 ) @@ -110,10 +67,10 @@ class ExportMission( if (generalInfo != null) { exportRepository.exportOdt( - service = "pam-iris-a", - id = "NAMO-2024-4", - startDateTime = "2024-02-12T12:34:56", - endDateTime = "2024-02-12T12:34:56", + service = mission.mission.openBy, + id = "pam" + mission.mission.id, + startDateTime = mission.mission.startDateTimeUtc, + endDateTime = mission.mission.endDateTimeUtc, presenceMer = presenceMer, presenceQuai = presenceQuai, indisponibilite = indisponibilite, From 95b37725e061778706b6f3d420622ffbcda88d90 Mon Sep 17 00:00:00 2001 From: aleckvincent Date: Wed, 28 Feb 2024 09:49:04 +0100 Subject: [PATCH 09/19] ajout presenceMer presenceQuai et indisponibilite --- .../mission/IRpnExportRepository.kt | 4 ++-- .../domain/use_cases/mission/ExportMission.kt | 24 ++++++++++++++----- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt index 01431dfeb..94ac5f8e2 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt @@ -13,8 +13,8 @@ interface IRpnExportRepository { presenceMer: Map, presenceQuai: Map, indisponibilite: Map, - nbJoursMer: Int, - dureeMission: Int, + nbJoursMer: Long, + dureeMission: Long, patrouilleEnv: Int, patrouilleMigrant: Int, distanceMilles: Float?, diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt index 121071e3d..31cf10056 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt @@ -41,28 +41,40 @@ class ExportMission( actions = statuses, ) - val presenceMer = mapOf( + val presenceMer = mutableMapOf( "navigationEffective" to (durations.find {it.status == ActionStatusType.NAVIGATING}?.value ?: 0), "mouillage" to (durations.find { it.status === ActionStatusType.ANCHORED }?.value ?: 0), - "total" to 226 + "total" to 0 ) - val presenceQuai = mapOf( + val totalPresenceMer = presenceMer.values.sum() + presenceMer["total"] = totalPresenceMer + + + val presenceQuai = mutableMapOf( "maintenance" to (durations.find { it.reason === ActionStatusReason.MAINTENANCE }?.value ?: 0), "meteo" to (durations.find { it.reason === ActionStatusReason.WEATHER }?.value ?: 0), "representation" to (durations.find { it.reason === ActionStatusReason.REPRESENTATION }?.value ?: 0), "adminFormation" to (durations.find { it.reason === ActionStatusReason.ADMINISTRATION }?.value ?: 0), "autre" to (durations.find { it.reason === ActionStatusReason.OTHER }?.value ?: 0), "contrPol" to (durations.find { it.reason === ActionStatusReason.HARBOUR_CONTROL }?.value ?: 0), - "total" to 25 + "total" to 0 ) - val indisponibilite = mapOf( + val totalQuai = presenceQuai.values.sum() + presenceQuai["total"] = totalQuai + + val indisponibilite = mutableMapOf( "technique" to (durations.find { it.reason === ActionStatusReason.TECHNICAL }?.value ?: 0), "personnel" to (durations.find { it.reason === ActionStatusReason.PERSONNEL }?.value ?: 0), "total" to 78 ) + val totalIndisponibilite = indisponibilite.values.sum() + indisponibilite["total"] = totalIndisponibilite + + val dureeMission = totalPresenceMer + totalQuai + totalIndisponibilite + if (generalInfo != null) { @@ -75,7 +87,7 @@ class ExportMission( presenceQuai = presenceQuai, indisponibilite = indisponibilite, nbJoursMer = 4, - dureeMission = 3, + dureeMission = dureeMission, patrouilleEnv = 2, patrouilleMigrant = 4, distanceMilles = generalInfo.distanceInNauticalMiles, From 1adde8bc93133eab87cd6ddec4d35dbedf0ee139 Mon Sep 17 00:00:00 2001 From: lwih Date: Wed, 28 Feb 2024 16:05:07 +0100 Subject: [PATCH 10/19] MissionActions now sorted in domain instead of bff --- .../domain/entities/mission/MissionEntity.kt | 28 +++++++++++++++---- .../domain/use_cases/mission/ExportMission.kt | 25 ++++++++--------- .../use_cases/mission/GetMissionById.kt | 23 +++++++++++++++ .../mission/status/GetStatusDurations.kt | 3 ++ .../infrastructure/bff/MissionController.kt | 13 ++------- .../infrastructure/bff/model/Mission.kt | 2 +- .../infrastructure/bff/model/action/Action.kt | 8 ------ 7 files changed, 65 insertions(+), 37 deletions(-) create mode 100644 backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/GetMissionById.kt diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/MissionEntity.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/MissionEntity.kt index 7ccee2870..3b0aadefa 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/MissionEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/MissionEntity.kt @@ -26,7 +26,7 @@ data class MissionEntity( val missionSource: MissionSourceEnum, val hasMissionOrder: Boolean, val isUnderJdp: Boolean, - var actions: List?, + var actions: List? = null, ) { constructor( envMission: ExtendedEnvMissionEntity, @@ -50,9 +50,27 @@ data class MissionEntity( missionSource = envMission.mission.missionSource, hasMissionOrder = envMission.mission.hasMissionOrder, isUnderJdp = envMission.mission.isUnderJdp, - actions = (envMission.actions?.map { MissionActionEntity.EnvAction(it) } ?: listOf()) + - (fishMissionActions?.map { MissionActionEntity.FishAction(it) } ?: listOf()) + - (navMission?.actions?.map { MissionActionEntity.NavAction(it) } ?: listOf()) - + actions = sortActions( + envMission.actions?.map { MissionActionEntity.EnvAction(it) } ?: emptyList(), + fishMissionActions?.map { MissionActionEntity.FishAction(it) } ?: emptyList(), + navMission?.actions?.map { MissionActionEntity.NavAction(it) } ?: emptyList() + ) ) + + companion object { + private fun sortActions( + envActions: List, + fishActions: List, + navActions: List + ): List { + return (envActions + fishActions + navActions).sortedBy { action -> + when (action) { + is MissionActionEntity.EnvAction -> action.envAction?.controlAction?.action?.actionStartDateTimeUtc + is MissionActionEntity.FishAction -> action.fishAction.controlAction?.action?.actionDatetimeUtc + is MissionActionEntity.NavAction -> action.navAction.startDateTimeUtc + } + } + } + } } + diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt index 31cf10056..bfb2bfb44 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt @@ -1,7 +1,7 @@ package fr.gouv.dgampa.rapportnav.domain.use_cases.mission import fr.gouv.dgampa.rapportnav.config.UseCase -import fr.gouv.dgampa.rapportnav.domain.entities.mission.ExtendedEnvMissionEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.generalInfo.MissionGeneralInfoEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusReason @@ -17,32 +17,31 @@ class ExportMission( private val exportRepository: IRpnExportRepository, private val getMissionGeneralInfoByMissionId: GetMissionGeneralInfoByMissionId, private val agentsCrewByMissionId: GetAgentsCrewByMissionId, - private val getEnvMissionById: GetEnvMissionById, + private val getMissionById: GetMissionById, private val navActionStatus: INavActionStatusRepository, private val getStatusDurations: GetStatusDurations, ) { fun exportOdt(missionId: Int) { - val mission: ExtendedEnvMissionEntity? = getEnvMissionById.execute(missionId = missionId) + val mission: MissionEntity? = getMissionById.execute(missionId = missionId) - if (mission?.mission == null) { + if (mission == null) { return - } - else { + } else { val generalInfo: MissionGeneralInfoEntity? = getMissionGeneralInfoByMissionId.execute(missionId) val agentsCrew: List = agentsCrewByMissionId.execute(missionId = missionId) val statuses = navActionStatus.findAllByMissionId(missionId = missionId).sortedBy { it.startDateTimeUtc } .map { it.toActionStatusEntity() } val durations = getStatusDurations.computeActionDurations( - missionStartDateTime = mission.mission.startDateTimeUtc, - missionEndDateTime = mission.mission.endDateTimeUtc, + missionStartDateTime = mission.startDateTimeUtc, + missionEndDateTime = mission.endDateTimeUtc, actions = statuses, ) val presenceMer = mutableMapOf( - "navigationEffective" to (durations.find {it.status == ActionStatusType.NAVIGATING}?.value ?: 0), + "navigationEffective" to (durations.find { it.status == ActionStatusType.NAVIGATING }?.value ?: 0), "mouillage" to (durations.find { it.status === ActionStatusType.ANCHORED }?.value ?: 0), "total" to 0 ) @@ -79,10 +78,10 @@ class ExportMission( if (generalInfo != null) { exportRepository.exportOdt( - service = mission.mission.openBy, - id = "pam" + mission.mission.id, - startDateTime = mission.mission.startDateTimeUtc, - endDateTime = mission.mission.endDateTimeUtc, + service = mission.openBy, + id = "pam" + mission.id, + startDateTime = mission.startDateTimeUtc, + endDateTime = mission.endDateTimeUtc, presenceMer = presenceMer, presenceQuai = presenceQuai, indisponibilite = indisponibilite, diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/GetMissionById.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/GetMissionById.kt new file mode 100644 index 000000000..f4af7ab4f --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/GetMissionById.kt @@ -0,0 +1,23 @@ +package fr.gouv.dgampa.rapportnav.domain.use_cases.mission + +import fr.gouv.dgampa.rapportnav.config.UseCase +import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionEntity +import org.slf4j.LoggerFactory + +@UseCase +class GetMissionById( + private val getEnvMissionById: GetEnvMissionById, + private val getFishActionsByMissionId: GetFishActionsByMissionId, + private val getNavMissionById: GetNavMissionById, +) { + private val logger = LoggerFactory.getLogger(GetMissionById::class.java) + + fun execute(missionId: Int): MissionEntity? { + val envMission = getEnvMissionById.execute(missionId = missionId) ?: return null + val fishMissionActions = getFishActionsByMissionId.execute(missionId = missionId) + val navMission = getNavMissionById.execute(missionId = missionId) + + return MissionEntity(envMission, navMission, fishMissionActions) + } + +} diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/status/GetStatusDurations.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/status/GetStatusDurations.kt index 8ad32e591..183f3d55e 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/status/GetStatusDurations.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/status/GetStatusDurations.kt @@ -1,10 +1,13 @@ package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.status +import fr.gouv.dgampa.rapportnav.config.UseCase import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusReason import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusType import java.time.ZonedDateTime + +@UseCase class GetStatusDurations() { data class ActionStatusWithDuration( diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/MissionController.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/MissionController.kt index 1fa9fa104..2adebccfd 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/MissionController.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/MissionController.kt @@ -1,7 +1,6 @@ package fr.gouv.dgampa.rapportnav.infrastructure.bff import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionActionEntity -import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionExportEntity import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.* import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.generalInfo.AddOrUpdateMissionGeneralInfo @@ -26,10 +25,8 @@ import java.util.* class MissionController( private val getUserFromToken: GetUserFromToken, private val getEnvMissions: GetEnvMissions, + private val getMissionById: GetMissionById, private val getNavMissionById: GetNavMissionById, - private val getEnvMissionById: GetEnvMissionById, - private val getFishActionsByMissionId: GetFishActionsByMissionId, - private val updateEnvMission: UpdateEnvMission, private val getMissionGeneralInfoByMissionId: GetMissionGeneralInfoByMissionId, private val addOrUpdateMissionGeneralInfo: AddOrUpdateMissionGeneralInfo, private val getControlUnitsForUser: GetControlUnitsForUser, @@ -50,7 +47,6 @@ class MissionController( controlUnits = getControlUnitsForUser.execute() )?.map { Mission.fromMissionEntity(it) }.orEmpty() - // temporarily add fictive missions val user = getUserFromToken.execute() val fakeMissions = fakeMissionData.getFakeMissionsforUser(user).map { Mission.fromMissionEntity(it) } @@ -77,11 +73,8 @@ class MissionController( fakeMission.actions?.plus(navMission.actions.map { MissionActionEntity.NavAction(it) }) Mission.fromMissionEntity(fakeMission) } else { - val envMission = getEnvMissionById.execute(missionId = missionId) ?: return null - val fishMissionActions = getFishActionsByMissionId.execute(missionId = missionId) - val navMission = getNavMissionById.execute(missionId = missionId) - - Mission.fromMissionEntity(MissionEntity(envMission, navMission, fishMissionActions)) + val mission = getMissionById.execute(missionId = missionId) + mission?.let { Mission.fromMissionEntity(it) } } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/Mission.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/Mission.kt index 4e06afb2a..23263e5a7 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/Mission.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/Mission.kt @@ -31,7 +31,7 @@ data class Mission( is MissionActionEntity.NavAction -> Action.fromNavAction(missionAction.navAction) } - }?.let { Action.sortForTimeline(it) } + } return Mission( id = mission.id, diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/action/Action.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/action/Action.kt index cb71a1433..a9fb42ea7 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/action/Action.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/model/action/Action.kt @@ -129,7 +129,6 @@ data class Action( } } - fun fromNavAction(navAction: NavActionEntity): Action? { var data: ActionData? = null when { @@ -155,12 +154,5 @@ data class Action( ) } - fun sortForTimeline(allActions: List?): List? { - return allActions?.sortedWith(compareByDescending { it.startDateTimeUtc } - .thenBy { it.data is NavActionStatus } - ) - } - - } } From 102e35c7084866bc9a95c986ee93ef9254e2de67 Mon Sep 17 00:00:00 2001 From: lwih Date: Wed, 28 Feb 2024 16:07:56 +0100 Subject: [PATCH 11/19] MissionActions now sorted descending --- .../dgampa/rapportnav/domain/entities/mission/MissionEntity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/MissionEntity.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/MissionEntity.kt index 3b0aadefa..f2489ba9d 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/MissionEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/MissionEntity.kt @@ -63,7 +63,7 @@ data class MissionEntity( fishActions: List, navActions: List ): List { - return (envActions + fishActions + navActions).sortedBy { action -> + return (envActions + fishActions + navActions).sortedByDescending { action -> when (action) { is MissionActionEntity.EnvAction -> action.envAction?.controlAction?.action?.actionStartDateTimeUtc is MissionActionEntity.FishAction -> action.fishAction.controlAction?.action?.actionDatetimeUtc From 7f43c7d2caa5e3711a407e215298aec9074dc15d Mon Sep 17 00:00:00 2001 From: lwih Date: Tue, 5 Mar 2024 12:33:05 +0100 Subject: [PATCH 12/19] Export mission - refactor use case --- .../mission/IRpnExportRepository.kt | 11 ++-- .../domain/use_cases/mission/ExportMission.kt | 63 ++++++++++--------- .../rapportnav1/APIRpnExportRepository.kt | 37 +++++------ 3 files changed, 56 insertions(+), 55 deletions(-) diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt index 94ac5f8e2..1e7a59e26 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt @@ -10,17 +10,16 @@ interface IRpnExportRepository { id: String, startDateTime: ZonedDateTime?, endDateTime: ZonedDateTime?, - presenceMer: Map, - presenceQuai: Map, - indisponibilite: Map, - nbJoursMer: Long, - dureeMission: Long, + presenceMer: Map, + presenceQuai: Map, + indisponibilite: Map, + nbJoursMer: Int, + dureeMission: Int, patrouilleEnv: Int, patrouilleMigrant: Int, distanceMilles: Float?, goMarine: Float?, essence: Float?, crew: List - ) } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt index bfb2bfb44..4ee333f88 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt @@ -22,6 +22,10 @@ class ExportMission( private val getStatusDurations: GetStatusDurations, ) { + private inline fun List.findDuration(predicate: (GetStatusDurations.ActionStatusWithDuration) -> Boolean): Int { + return find(predicate)?.value?.toInt() ?: 0 + } + fun exportOdt(missionId: Int) { val mission: MissionEntity? = getMissionById.execute(missionId = missionId) @@ -34,46 +38,43 @@ class ExportMission( val statuses = navActionStatus.findAllByMissionId(missionId = missionId).sortedBy { it.startDateTimeUtc } .map { it.toActionStatusEntity() } + val durations = getStatusDurations.computeActionDurations( missionStartDateTime = mission.startDateTimeUtc, missionEndDateTime = mission.endDateTimeUtc, actions = statuses, ) - val presenceMer = mutableMapOf( - "navigationEffective" to (durations.find { it.status == ActionStatusType.NAVIGATING }?.value ?: 0), - "mouillage" to (durations.find { it.status === ActionStatusType.ANCHORED }?.value ?: 0), + val atSeaDurations = mapOf( + "navigationEffective" to durations.findDuration { it.status == ActionStatusType.NAVIGATING }, + "mouillage" to durations.findDuration { it.status == ActionStatusType.ANCHORED }, "total" to 0 - ) - - val totalPresenceMer = presenceMer.values.sum() - presenceMer["total"] = totalPresenceMer + ).toMutableMap() + atSeaDurations["total"] = atSeaDurations.values.sum() - val presenceQuai = mutableMapOf( - "maintenance" to (durations.find { it.reason === ActionStatusReason.MAINTENANCE }?.value ?: 0), - "meteo" to (durations.find { it.reason === ActionStatusReason.WEATHER }?.value ?: 0), - "representation" to (durations.find { it.reason === ActionStatusReason.REPRESENTATION }?.value ?: 0), - "adminFormation" to (durations.find { it.reason === ActionStatusReason.ADMINISTRATION }?.value ?: 0), - "autre" to (durations.find { it.reason === ActionStatusReason.OTHER }?.value ?: 0), - "contrPol" to (durations.find { it.reason === ActionStatusReason.HARBOUR_CONTROL }?.value ?: 0), + val dockingDurations = mapOf( + "maintenance" to durations.findDuration { it.reason == ActionStatusReason.MAINTENANCE }, + "meteo" to durations.findDuration { it.reason == ActionStatusReason.WEATHER }, + "representation" to durations.findDuration { it.reason == ActionStatusReason.REPRESENTATION }, + "adminFormation" to durations.findDuration { it.reason == ActionStatusReason.ADMINISTRATION }, + "autre" to durations.findDuration { it.reason == ActionStatusReason.OTHER }, + "contrPol" to durations.findDuration { it.reason == ActionStatusReason.HARBOUR_CONTROL }, "total" to 0 - ) - - val totalQuai = presenceQuai.values.sum() - presenceQuai["total"] = totalQuai + ).toMutableMap() + dockingDurations["total"] = dockingDurations.values.sum() - val indisponibilite = mutableMapOf( - "technique" to (durations.find { it.reason === ActionStatusReason.TECHNICAL }?.value ?: 0), - "personnel" to (durations.find { it.reason === ActionStatusReason.PERSONNEL }?.value ?: 0), - "total" to 78 - ) - val totalIndisponibilite = indisponibilite.values.sum() - indisponibilite["total"] = totalIndisponibilite - - val dureeMission = totalPresenceMer + totalQuai + totalIndisponibilite + val unavailabilityDurations = mapOf( + "technique" to durations.findDuration { it.reason == ActionStatusReason.TECHNICAL }, + "personnel" to durations.findDuration { it.reason == ActionStatusReason.PERSONNEL }, + "total" to 0 + ).toMutableMap() + unavailabilityDurations["total"] = unavailabilityDurations.values.sum() + val missionDuration = + (atSeaDurations["total"] ?: 0) + (dockingDurations["total"] ?: 0) + (unavailabilityDurations["total"] + ?: 0) if (generalInfo != null) { @@ -82,11 +83,11 @@ class ExportMission( id = "pam" + mission.id, startDateTime = mission.startDateTimeUtc, endDateTime = mission.endDateTimeUtc, - presenceMer = presenceMer, - presenceQuai = presenceQuai, - indisponibilite = indisponibilite, + presenceMer = atSeaDurations, + presenceQuai = dockingDurations, + indisponibilite = unavailabilityDurations, nbJoursMer = 4, - dureeMission = dureeMission, + dureeMission = missionDuration, patrouilleEnv = 2, patrouilleMigrant = 4, distanceMilles = generalInfo.distanceInNauticalMiles, diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/APIRpnExportRepository.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/APIRpnExportRepository.kt index e7c8d8250..2fcc53948 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/APIRpnExportRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/APIRpnExportRepository.kt @@ -11,16 +11,17 @@ import java.net.http.HttpClient import java.net.http.HttpRequest import java.net.http.HttpRequest.BodyPublishers import java.net.http.HttpResponse.BodyHandlers +import java.time.ZonedDateTime @Repository class APIRpnExportRepository( private val mapper: ObjectMapper -): IRpnExportRepository { +) : IRpnExportRepository { override fun exportOdt( service: String?, id: String, - startDateTime: String?, - endDateTime: String?, + startDateTime: ZonedDateTime?, + endDateTime: ZonedDateTime?, presenceMer: Map, presenceQuai: Map, indisponibilite: Map, @@ -37,21 +38,21 @@ class APIRpnExportRepository( val client = HttpClient.newHttpClient() val content = RpnExportOdtOutput( - service, - id, - startDateTime, - endDateTime, - presenceMer, - presenceQuai, - indisponibilite, - nbJoursMer, - dureeMission, - patrouilleEnv, - patrouilleMigrant, - distanceMilles, - goMarine, - essence, - crew + service = service, + id = id, + startDateTime = startDateTime?.toString(), + endDateTime = endDateTime?.toString(), + presenceMer = presenceMer, + presenceQuai = presenceQuai, + indisponibilite = indisponibilite, + nbJoursMer = nbJoursMer, + dureeMission = dureeMission, + patrouilleEnv = patrouilleEnv, + patrouilleMigrant = patrouilleMigrant, + distanceMilles = distanceMilles, + goMarine = goMarine, + essence = essence, + crew = crew ) val gson = Gson(); From 13e6d0c1a3f2f334cbfa7451bdca9ec545a880e0 Mon Sep 17 00:00:00 2001 From: lwih Date: Tue, 5 Mar 2024 16:23:52 +0100 Subject: [PATCH 13/19] Export mission - format timeline --- .../domain/use_cases/mission/ExportMission.kt | 161 +++++++++++++----- .../mission/action/GroupActionByDate.kt | 21 +++ .../mission/action/GroupActionByDateTests.kt | 67 ++++++++ .../mission/action/EnvActionControlMock.kt | 47 +++++ .../mission/action/FishActionControlMock.kt | 28 +++ .../mission/action/NavActionStatusMock.kt | 20 +++ 6 files changed, 306 insertions(+), 38 deletions(-) create mode 100644 backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/action/GroupActionByDate.kt create mode 100644 backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/action/GroupActionByDateTests.kt create mode 100644 backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/EnvActionControlMock.kt create mode 100644 backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/FishActionControlMock.kt create mode 100644 backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionStatusMock.kt diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt index 4ee333f88..328f45a94 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt @@ -1,16 +1,24 @@ package fr.gouv.dgampa.rapportnav.domain.use_cases.mission import fr.gouv.dgampa.rapportnav.config.UseCase +import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionActionEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.EnvActionControlEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.MissionAction +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionType +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.NavActionEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.generalInfo.MissionGeneralInfoEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusReason import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusType import fr.gouv.dgampa.rapportnav.domain.repositories.mission.IRpnExportRepository import fr.gouv.dgampa.rapportnav.domain.repositories.mission.action.INavActionStatusRepository +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.action.GroupActionByDate import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.crew.GetAgentsCrewByMissionId import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.generalInfo.GetMissionGeneralInfoByMissionId import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.status.GetStatusDurations +import java.time.LocalDate @UseCase class ExportMission( @@ -20,12 +28,118 @@ class ExportMission( private val getMissionById: GetMissionById, private val navActionStatus: INavActionStatusRepository, private val getStatusDurations: GetStatusDurations, + private val groupActionByDate: GroupActionByDate, ) { private inline fun List.findDuration(predicate: (GetStatusDurations.ActionStatusWithDuration) -> Boolean): Int { return find(predicate)?.value?.toInt() ?: 0 } + fun computeDurations(mission: MissionEntity, statuses: List): Map> { + val durations = getStatusDurations.computeActionDurations( + missionStartDateTime = mission.startDateTimeUtc, + missionEndDateTime = mission.endDateTimeUtc, + actions = statuses, + ) + + val atSeaDurations = mapOf( + "navigationEffective" to durations.findDuration { it.status == ActionStatusType.NAVIGATING }, + "mouillage" to durations.findDuration { it.status == ActionStatusType.ANCHORED }, + "total" to 0 + ).toMutableMap() + atSeaDurations["total"] = atSeaDurations.values.sum() + + val dockingDurations = mapOf( + "maintenance" to durations.findDuration { it.reason == ActionStatusReason.MAINTENANCE }, + "meteo" to durations.findDuration { it.reason == ActionStatusReason.WEATHER }, + "representation" to durations.findDuration { it.reason == ActionStatusReason.REPRESENTATION }, + "adminFormation" to durations.findDuration { it.reason == ActionStatusReason.ADMINISTRATION }, + "autre" to durations.findDuration { it.reason == ActionStatusReason.OTHER }, + "contrPol" to durations.findDuration { it.reason == ActionStatusReason.HARBOUR_CONTROL }, + "total" to 0 + ).toMutableMap() + dockingDurations["total"] = dockingDurations.values.sum() + + val unavailabilityDurations = mapOf( + "technique" to durations.findDuration { it.reason == ActionStatusReason.TECHNICAL }, + "personnel" to durations.findDuration { it.reason == ActionStatusReason.PERSONNEL }, + "total" to 0 + ).toMutableMap() + unavailabilityDurations["total"] = unavailabilityDurations.values.sum() + + return mapOf( + "atSeaDurations" to atSeaDurations.toMap(), + "dockingDurations" to dockingDurations.toMap(), + "unavailabilityDurations" to unavailabilityDurations.toMap() + ) + } + + + fun formatEnvControlForTimeline(action: EnvActionControlEntity): String { + // Code to format EnvActionControlEntity + return "EnvControlForTimeline" + } + + fun formatFishControlForTimeline(action: MissionAction): String { + // Code to format MissionActionEntity (Fish) + return "FishControlForTimeline" + } + + fun formatNavNoteForTimeline(action: NavActionEntity): String { + // Code to format NavActionEntity with ActionType.NOTE + return "NavNoteForTimeline" + } + + fun formatNavStatusForTimeline(action: NavActionEntity): String { + // Code to format NavActionEntity with ActionType.STATUS + return "NavStatusForTimeline" + } + + fun formatNavControlForTimeline(action: NavActionEntity): String { + // Code to format NavActionEntity with ActionType.CONTROL + return "NavControlForTimeline" + } + + fun formatActionsForTimeline(actions: List?): Map>? { + + if (actions.isNullOrEmpty()) { + return null + } + // Group actions by date + val groupedActions = groupActionByDate.execute(actions = actions) + + // Map each group to list of formatted strings + val formattedActions = groupedActions?.mapValues { (_, actionsOnDate) -> + actionsOnDate.mapNotNull { action -> + when (action) { + is MissionActionEntity.EnvAction -> { + if (action.envAction?.controlAction?.action is EnvActionControlEntity) { + formatEnvControlForTimeline(action.envAction.controlAction.action) + } else null + } + + is MissionActionEntity.FishAction -> { + if (action.fishAction.controlAction?.action is MissionAction) { + formatFishControlForTimeline(action.fishAction.controlAction.action) + } else null + } + + is MissionActionEntity.NavAction -> { + val navAction: NavActionEntity = action.navAction + when (navAction.actionType) { + ActionType.NOTE -> formatNavNoteForTimeline(navAction) + ActionType.STATUS -> formatNavStatusForTimeline(navAction) + ActionType.CONTROL -> formatNavControlForTimeline(navAction) + else -> null + } + } + } + } + } + + return formattedActions + } + fun exportOdt(missionId: Int) { val mission: MissionEntity? = getMissionById.execute(missionId = missionId) @@ -39,43 +153,14 @@ class ExportMission( .map { it.toActionStatusEntity() } - val durations = getStatusDurations.computeActionDurations( - missionStartDateTime = mission.startDateTimeUtc, - missionEndDateTime = mission.endDateTimeUtc, - actions = statuses, - ) - - val atSeaDurations = mapOf( - "navigationEffective" to durations.findDuration { it.status == ActionStatusType.NAVIGATING }, - "mouillage" to durations.findDuration { it.status == ActionStatusType.ANCHORED }, - "total" to 0 - ).toMutableMap() - atSeaDurations["total"] = atSeaDurations.values.sum() - - - val dockingDurations = mapOf( - "maintenance" to durations.findDuration { it.reason == ActionStatusReason.MAINTENANCE }, - "meteo" to durations.findDuration { it.reason == ActionStatusReason.WEATHER }, - "representation" to durations.findDuration { it.reason == ActionStatusReason.REPRESENTATION }, - "adminFormation" to durations.findDuration { it.reason == ActionStatusReason.ADMINISTRATION }, - "autre" to durations.findDuration { it.reason == ActionStatusReason.OTHER }, - "contrPol" to durations.findDuration { it.reason == ActionStatusReason.HARBOUR_CONTROL }, - "total" to 0 - ).toMutableMap() - dockingDurations["total"] = dockingDurations.values.sum() - - - val unavailabilityDurations = mapOf( - "technique" to durations.findDuration { it.reason == ActionStatusReason.TECHNICAL }, - "personnel" to durations.findDuration { it.reason == ActionStatusReason.PERSONNEL }, - "total" to 0 - ).toMutableMap() - unavailabilityDurations["total"] = unavailabilityDurations.values.sum() + val durations = computeDurations(mission, statuses) - val missionDuration = - (atSeaDurations["total"] ?: 0) + (dockingDurations["total"] ?: 0) + (unavailabilityDurations["total"] - ?: 0) + // TODO quelle formule utiliser? celle ci ou plutot missionEnd - missionStart ? + val missionDuration = (durations["atSeaDurations"]?.get("total") ?: 0) + + (durations["dockingDurations"]?.get("total") ?: 0) + + (durations["unavailabilityDurations"]?.get("total") ?: 0) + val timeline = formatActionsForTimeline(mission.actions) if (generalInfo != null) { exportRepository.exportOdt( @@ -83,9 +168,9 @@ class ExportMission( id = "pam" + mission.id, startDateTime = mission.startDateTimeUtc, endDateTime = mission.endDateTimeUtc, - presenceMer = atSeaDurations, - presenceQuai = dockingDurations, - indisponibilite = unavailabilityDurations, + presenceMer = durations["atSeaDurations"].orEmpty(), + presenceQuai = durations["dockingDurations"].orEmpty(), + indisponibilite = durations["unavailabilityDurations"].orEmpty(), nbJoursMer = 4, dureeMission = missionDuration, patrouilleEnv = 2, diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/action/GroupActionByDate.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/action/GroupActionByDate.kt new file mode 100644 index 000000000..3b17a5f55 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/action/GroupActionByDate.kt @@ -0,0 +1,21 @@ +package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.action + +import fr.gouv.dgampa.rapportnav.config.UseCase +import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionActionEntity +import java.time.LocalDate + +@UseCase +class GroupActionByDate { + fun execute(actions: List?): Map>? { + return actions?.groupBy { action -> + when (action) { + is MissionActionEntity.EnvAction -> action.envAction?.controlAction?.action?.actionStartDateTimeUtc?.toLocalDate() + is MissionActionEntity.FishAction -> action.fishAction.controlAction?.action?.actionDatetimeUtc?.toLocalDate() + is MissionActionEntity.NavAction -> action.navAction.startDateTimeUtc.toLocalDate() + else -> null // Handle other types of actions, if any + } + }?.mapNotNull { (date, actions) -> + date?.let { it to actions } + }?.toMap() + } +} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/action/GroupActionByDateTests.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/action/GroupActionByDateTests.kt new file mode 100644 index 000000000..20c132bd2 --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/action/GroupActionByDateTests.kt @@ -0,0 +1,67 @@ +package fr.gouv.gmampa.rapportnav.domain.use_cases.mission.action + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionActionEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ExtendedEnvActionEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ExtendedFishActionEntity +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.action.GroupActionByDate +import fr.gouv.gmampa.rapportnav.mocks.mission.action.EnvActionControlMock +import fr.gouv.gmampa.rapportnav.mocks.mission.action.FishActionControlMock +import fr.gouv.gmampa.rapportnav.mocks.mission.action.NavActionStatusMock +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.ZoneOffset +import java.time.ZonedDateTime + +class GroupActionByDateTest { + + private val groupActionByDate = GroupActionByDate() + + @Test + fun `execute should return null when actions is null`() { + val result = groupActionByDate.execute(null) + assertThat(result).isNull() + } + + @Test + fun `execute should group actions by day`() { + // Mock mission actions + val action1 = MissionActionEntity.EnvAction( + ExtendedEnvActionEntity.fromEnvActionEntity( + EnvActionControlMock.create( + actionStartDateTimeUtc = ZonedDateTime.of( + LocalDateTime.of(2022, 1, 1, 12, 0), ZoneOffset.UTC + ) + ) + ) + ) + + val action2 = MissionActionEntity.FishAction( + ExtendedFishActionEntity.fromMissionAction( + FishActionControlMock.create( + missionId = 1, actionDatetimeUtc = ZonedDateTime.of( + LocalDateTime.of( + 2022, 1, 1, 13, 0 + ), ZoneOffset.UTC + ) + ) + ) + ) + + val action3 = MissionActionEntity.NavAction( + NavActionStatusMock.create() + ) + + val actions = listOf(action1, action2, action3) + + val result = groupActionByDate.execute(actions) + + assertThat(result).isNotNull() + assertThat(result!!.size).isEqualTo(2) + assertThat(result[LocalDate.of(2022, 1, 1)]?.size).isEqualTo(2) + assertThat(result[LocalDate.of(2022, 1, 2)]?.size).isEqualTo(1) + } + + +} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/EnvActionControlMock.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/EnvActionControlMock.kt new file mode 100644 index 000000000..8bfe69109 --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/EnvActionControlMock.kt @@ -0,0 +1,47 @@ +package fr.gouv.gmampa.rapportnav.mocks.mission.action + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.* +import org.locationtech.jts.geom.Geometry +import java.time.ZonedDateTime +import java.util.* + +object EnvActionControlMock { + fun create( + id: UUID = UUID.randomUUID(), + actionStartDateTimeUtc: ZonedDateTime? = null, + actionEndDateTimeUtc: ZonedDateTime? = null, + geom: Geometry? = null, + facade: String? = null, + department: String? = null, + isAdministrativeControl: Boolean? = null, + isComplianceWithWaterRegulationsControl: Boolean? = null, + isSafetyEquipmentAndStandardsComplianceControl: Boolean? = null, + isSeafarersControl: Boolean? = null, + themes: List? = emptyList(), + observations: String? = null, + actionNumberOfControls: Int? = null, + actionTargetType: ActionTargetTypeEnum? = null, + vehicleType: VehicleTypeEnum? = null, + infractions: List? = emptyList(), + ): EnvActionControlEntity { + return EnvActionControlEntity( + id = id, + actionStartDateTimeUtc = actionStartDateTimeUtc, + actionEndDateTimeUtc = actionEndDateTimeUtc, + geom = geom, + facade = facade, + department = department, + isAdministrativeControl = isAdministrativeControl, + isComplianceWithWaterRegulationsControl = isComplianceWithWaterRegulationsControl, + isSafetyEquipmentAndStandardsComplianceControl = isSafetyEquipmentAndStandardsComplianceControl, + isSeafarersControl = isSeafarersControl, + themes = themes, + observations = observations, + actionNumberOfControls = actionNumberOfControls, + actionTargetType = actionTargetType, + vehicleType = vehicleType, + infractions = infractions + // Set other properties to their default values or mocks as needed + ) + } +} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/FishActionControlMock.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/FishActionControlMock.kt new file mode 100644 index 000000000..962967fc4 --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/FishActionControlMock.kt @@ -0,0 +1,28 @@ +package fr.gouv.gmampa.rapportnav.mocks.mission.action + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.MissionAction +import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.MissionActionType +import java.time.ZonedDateTime +import java.util.* + +object FishActionControlMock { + fun create( + missionId: Int, + actionDatetimeUtc: ZonedDateTime, + actionType: MissionActionType = MissionActionType.SEA_CONTROL, + isDeleted: Boolean = false, + hasSomeGearsSeized: Boolean = false, + hasSomeSpeciesSeized: Boolean = false, + ): MissionAction { + return MissionAction( + id = UUID.randomUUID().hashCode(), + missionId = missionId, + actionDatetimeUtc = actionDatetimeUtc, + actionType = actionType, + isDeleted = isDeleted, + hasSomeGearsSeized = hasSomeGearsSeized, + hasSomeSpeciesSeized = hasSomeSpeciesSeized + // Set other properties to their default values or mocks as needed + ) + } +} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionStatusMock.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionStatusMock.kt new file mode 100644 index 000000000..1e9f9a416 --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionStatusMock.kt @@ -0,0 +1,20 @@ +package fr.gouv.gmampa.rapportnav.mocks.mission.action + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionType +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.NavActionEntity +import java.time.LocalDateTime +import java.time.ZoneOffset +import java.time.ZonedDateTime +import java.util.* + +object NavActionStatusMock { + fun create(): NavActionEntity { + return NavActionEntity( + id = UUID.randomUUID(), + missionId = 1, + startDateTimeUtc = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 12, 0), ZoneOffset.UTC), + endDateTimeUtc = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 14, 0), ZoneOffset.UTC), + actionType = ActionType.STATUS, + ) + } +} From 0140a4aec6863217775ae132c62dd03a3a460d55 Mon Sep 17 00:00:00 2001 From: aleckvincent Date: Tue, 5 Mar 2024 17:07:18 +0100 Subject: [PATCH 14/19] add MissionExportEntity as return result for missionExport --- .../mission/IRpnExportRepository.kt | 7 +++- .../domain/use_cases/mission/ExportMission.kt | 42 +++++++++---------- .../rapportnav1/APIRpnExportRepository.kt | 18 +++++--- .../ExportMissionODTInput.kt} | 8 ++-- 4 files changed, 43 insertions(+), 32 deletions(-) rename backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/adapters/{outputs/RpnExportOdtOutput.kt => inputs/ExportMissionODTInput.kt} (78%) diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt index 1e7a59e26..384a46d7f 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt @@ -1,6 +1,8 @@ package fr.gouv.dgampa.rapportnav.domain.repositories.mission import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionExportEntity +import java.time.LocalDate import java.time.ZonedDateTime interface IRpnExportRepository { @@ -20,6 +22,7 @@ interface IRpnExportRepository { distanceMilles: Float?, goMarine: Float?, essence: Float?, - crew: List - ) + crew: List, + timeline: Map>? + ) : MissionExportEntity? } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt index 328f45a94..686565983 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt @@ -9,6 +9,7 @@ import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatus import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionType import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.NavActionEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionExportEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.generalInfo.MissionGeneralInfoEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusReason import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusType @@ -140,12 +141,12 @@ class ExportMission( return formattedActions } - fun exportOdt(missionId: Int) { + fun exportOdt(missionId: Int): MissionExportEntity? { val mission: MissionEntity? = getMissionById.execute(missionId = missionId) if (mission == null) { - return + return null } else { val generalInfo: MissionGeneralInfoEntity? = getMissionGeneralInfoByMissionId.execute(missionId) val agentsCrew: List = agentsCrewByMissionId.execute(missionId = missionId) @@ -162,25 +163,24 @@ class ExportMission( val timeline = formatActionsForTimeline(mission.actions) - if (generalInfo != null) { - exportRepository.exportOdt( - service = mission.openBy, - id = "pam" + mission.id, - startDateTime = mission.startDateTimeUtc, - endDateTime = mission.endDateTimeUtc, - presenceMer = durations["atSeaDurations"].orEmpty(), - presenceQuai = durations["dockingDurations"].orEmpty(), - indisponibilite = durations["unavailabilityDurations"].orEmpty(), - nbJoursMer = 4, - dureeMission = missionDuration, - patrouilleEnv = 2, - patrouilleMigrant = 4, - distanceMilles = generalInfo.distanceInNauticalMiles, - goMarine = generalInfo.consumedGOInLiters, - essence = generalInfo.consumedFuelInLiters, - crew = agentsCrew, - ) - } + return exportRepository.exportOdt( + service = mission.openBy, + id = "pam" + mission.id, + startDateTime = mission.startDateTimeUtc, + endDateTime = mission.endDateTimeUtc, + presenceMer = durations["atSeaDurations"].orEmpty(), + presenceQuai = durations["dockingDurations"].orEmpty(), + indisponibilite = durations["unavailabilityDurations"].orEmpty(), + nbJoursMer = 0, + dureeMission = missionDuration, + patrouilleEnv = 0, + patrouilleMigrant = 0, + distanceMilles = generalInfo?.distanceInNauticalMiles, + goMarine = generalInfo?.consumedGOInLiters, + essence = generalInfo?.consumedFuelInLiters, + crew = agentsCrew, + timeline = timeline + ) } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/APIRpnExportRepository.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/APIRpnExportRepository.kt index 2fcc53948..be5677b7a 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/APIRpnExportRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/APIRpnExportRepository.kt @@ -3,14 +3,16 @@ package fr.gouv.dgampa.rapportnav.infrastructure.rapportnav1 import com.fasterxml.jackson.databind.ObjectMapper import com.google.gson.Gson import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionExportEntity import fr.gouv.dgampa.rapportnav.domain.repositories.mission.IRpnExportRepository -import fr.gouv.dgampa.rapportnav.infrastructure.rapportnav1.adapters.outputs.RpnExportOdtOutput +import fr.gouv.dgampa.rapportnav.infrastructure.rapportnav1.adapters.inputs.ExportMissionODTInput import org.springframework.stereotype.Repository import java.net.URI import java.net.http.HttpClient import java.net.http.HttpRequest import java.net.http.HttpRequest.BodyPublishers import java.net.http.HttpResponse.BodyHandlers +import java.time.LocalDate import java.time.ZonedDateTime @Repository @@ -32,12 +34,13 @@ class APIRpnExportRepository( distanceMilles: Float?, goMarine: Float?, essence: Float?, - crew: List - ) { - val url = "http://127.0.0.1:8000/public_api/export/odt" + crew: List, + timeline: Map>? + ): MissionExportEntity? { + val url = "https://rapport-mission-dcs.din.developpement-durable.gouv.fr/public_api/export/odt" val client = HttpClient.newHttpClient() - val content = RpnExportOdtOutput( + val content = ExportMissionODTInput( service = service, id = id, startDateTime = startDateTime?.toString(), @@ -52,7 +55,8 @@ class APIRpnExportRepository( distanceMilles = distanceMilles, goMarine = goMarine, essence = essence, - crew = crew + crew = crew, + timeline = timeline ) val gson = Gson(); @@ -65,5 +69,7 @@ class APIRpnExportRepository( .build() val response = client.send(request, BodyHandlers.ofString()) + + return gson.fromJson(response.body(), MissionExportEntity::class.java) } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/adapters/outputs/RpnExportOdtOutput.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/adapters/inputs/ExportMissionODTInput.kt similarity index 78% rename from backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/adapters/outputs/RpnExportOdtOutput.kt rename to backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/adapters/inputs/ExportMissionODTInput.kt index 97fabf365..5d8d3b463 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/adapters/outputs/RpnExportOdtOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/adapters/inputs/ExportMissionODTInput.kt @@ -1,8 +1,9 @@ -package fr.gouv.dgampa.rapportnav.infrastructure.rapportnav1.adapters.outputs +package fr.gouv.dgampa.rapportnav.infrastructure.rapportnav1.adapters.inputs import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity +import java.time.LocalDate -class RpnExportOdtOutput ( +class ExportMissionODTInput ( val service: String?, val id: String?, val startDateTime: String?, @@ -17,5 +18,6 @@ class RpnExportOdtOutput ( val distanceMilles: Float?, val goMarine: Float?, val essence: Float?, - val crew: List + val crew: List, + val timeline: Map>? ) From 8f2f6ea8b7393276df165eea36ed3150d65173d7 Mon Sep 17 00:00:00 2001 From: lwih Date: Wed, 6 Mar 2024 10:00:36 +0100 Subject: [PATCH 15/19] Export mission - update export function arg format --- .../mission/IRpnExportRepository.kt | 39 ++++++------- .../domain/use_cases/mission/ExportMission.kt | 23 +++++--- .../rapportnav1/APIRpnExportRepository.kt | 55 ++++++------------- .../mission/action/GroupActionByDateTests.kt | 6 +- .../mission/action/NavActionFreeNoteMock.kt | 21 +++++++ 5 files changed, 80 insertions(+), 64 deletions(-) create mode 100644 backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionFreeNoteMock.kt diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt index 384a46d7f..b94c8f36f 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/repositories/mission/IRpnExportRepository.kt @@ -5,24 +5,25 @@ import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionExpor import java.time.LocalDate import java.time.ZonedDateTime -interface IRpnExportRepository { +data class ExportParams( + val service: String?, + val id: String, + val startDateTime: ZonedDateTime?, + val endDateTime: ZonedDateTime?, + val presenceMer: Map, + val presenceQuai: Map, + val indisponibilite: Map, + val nbJoursMer: Int, + val dureeMission: Int, + val patrouilleEnv: Int, + val patrouilleMigrant: Int, + val distanceMilles: Float?, + val goMarine: Float?, + val essence: Float?, + val crew: List, + val timeline: Map>? +) - fun exportOdt( - service: String?, - id: String, - startDateTime: ZonedDateTime?, - endDateTime: ZonedDateTime?, - presenceMer: Map, - presenceQuai: Map, - indisponibilite: Map, - nbJoursMer: Int, - dureeMission: Int, - patrouilleEnv: Int, - patrouilleMigrant: Int, - distanceMilles: Float?, - goMarine: Float?, - essence: Float?, - crew: List, - timeline: Map>? - ) : MissionExportEntity? +interface IRpnExportRepository { + fun exportOdt(params: ExportParams): MissionExportEntity? } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt index 686565983..7118d0c1a 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt @@ -5,6 +5,7 @@ import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionActionEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.EnvActionControlEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.MissionAction +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionFreeNoteEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionType import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.NavActionEntity @@ -13,6 +14,7 @@ import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionExpor import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.generalInfo.MissionGeneralInfoEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusReason import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusType +import fr.gouv.dgampa.rapportnav.domain.repositories.mission.ExportParams import fr.gouv.dgampa.rapportnav.domain.repositories.mission.IRpnExportRepository import fr.gouv.dgampa.rapportnav.domain.repositories.mission.action.INavActionStatusRepository import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.action.GroupActionByDate @@ -86,11 +88,15 @@ class ExportMission( return "FishControlForTimeline" } - fun formatNavNoteForTimeline(action: NavActionEntity): String { - // Code to format NavActionEntity with ActionType.NOTE - return "NavNoteForTimeline" + fun formatNavNoteForTimeline(action: ActionFreeNoteEntity?): String? { + return action?.let { + val startTime = action.startDateTimeUtc.toLocalTime().toString().padStart(5, '0') + val observation = action.observations ?: "" + return "$startTime - $observation" + } } + fun formatNavStatusForTimeline(action: NavActionEntity): String { // Code to format NavActionEntity with ActionType.STATUS return "NavStatusForTimeline" @@ -128,7 +134,7 @@ class ExportMission( is MissionActionEntity.NavAction -> { val navAction: NavActionEntity = action.navAction when (navAction.actionType) { - ActionType.NOTE -> formatNavNoteForTimeline(navAction) + ActionType.NOTE -> formatNavNoteForTimeline(navAction.freeNoteAction) ActionType.STATUS -> formatNavStatusForTimeline(navAction) ActionType.CONTROL -> formatNavControlForTimeline(navAction) else -> null @@ -145,8 +151,8 @@ class ExportMission( val mission: MissionEntity? = getMissionById.execute(missionId = missionId) - if (mission == null) { - return null + return if (mission == null) { + null } else { val generalInfo: MissionGeneralInfoEntity? = getMissionGeneralInfoByMissionId.execute(missionId) val agentsCrew: List = agentsCrewByMissionId.execute(missionId = missionId) @@ -163,7 +169,7 @@ class ExportMission( val timeline = formatActionsForTimeline(mission.actions) - return exportRepository.exportOdt( + val exportParams = ExportParams( service = mission.openBy, id = "pam" + mission.id, startDateTime = mission.startDateTimeUtc, @@ -181,6 +187,9 @@ class ExportMission( crew = agentsCrew, timeline = timeline ) +// return null + exportRepository.exportOdt(exportParams) + } } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/APIRpnExportRepository.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/APIRpnExportRepository.kt index be5677b7a..3c3fb2ff7 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/APIRpnExportRepository.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/rapportnav1/APIRpnExportRepository.kt @@ -2,8 +2,8 @@ package fr.gouv.dgampa.rapportnav.infrastructure.rapportnav1 import com.fasterxml.jackson.databind.ObjectMapper import com.google.gson.Gson -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionExportEntity +import fr.gouv.dgampa.rapportnav.domain.repositories.mission.ExportParams import fr.gouv.dgampa.rapportnav.domain.repositories.mission.IRpnExportRepository import fr.gouv.dgampa.rapportnav.infrastructure.rapportnav1.adapters.inputs.ExportMissionODTInput import org.springframework.stereotype.Repository @@ -12,51 +12,32 @@ import java.net.http.HttpClient import java.net.http.HttpRequest import java.net.http.HttpRequest.BodyPublishers import java.net.http.HttpResponse.BodyHandlers -import java.time.LocalDate -import java.time.ZonedDateTime @Repository class APIRpnExportRepository( private val mapper: ObjectMapper ) : IRpnExportRepository { - override fun exportOdt( - service: String?, - id: String, - startDateTime: ZonedDateTime?, - endDateTime: ZonedDateTime?, - presenceMer: Map, - presenceQuai: Map, - indisponibilite: Map, - nbJoursMer: Int, - dureeMission: Int, - patrouilleEnv: Int, - patrouilleMigrant: Int, - distanceMilles: Float?, - goMarine: Float?, - essence: Float?, - crew: List, - timeline: Map>? - ): MissionExportEntity? { + override fun exportOdt(params: ExportParams): MissionExportEntity? { val url = "https://rapport-mission-dcs.din.developpement-durable.gouv.fr/public_api/export/odt" val client = HttpClient.newHttpClient() val content = ExportMissionODTInput( - service = service, - id = id, - startDateTime = startDateTime?.toString(), - endDateTime = endDateTime?.toString(), - presenceMer = presenceMer, - presenceQuai = presenceQuai, - indisponibilite = indisponibilite, - nbJoursMer = nbJoursMer, - dureeMission = dureeMission, - patrouilleEnv = patrouilleEnv, - patrouilleMigrant = patrouilleMigrant, - distanceMilles = distanceMilles, - goMarine = goMarine, - essence = essence, - crew = crew, - timeline = timeline + service = params.service, + id = params.id, + startDateTime = params.startDateTime?.toString(), + endDateTime = params.endDateTime?.toString(), + presenceMer = params.presenceMer, + presenceQuai = params.presenceQuai, + indisponibilite = params.indisponibilite, + nbJoursMer = params.nbJoursMer, + dureeMission = params.dureeMission, + patrouilleEnv = params.patrouilleEnv, + patrouilleMigrant = params.patrouilleMigrant, + distanceMilles = params.distanceMilles, + goMarine = params.goMarine, + essence = params.essence, + crew = params.crew, + timeline = params.timeline ) val gson = Gson(); diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/action/GroupActionByDateTests.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/action/GroupActionByDateTests.kt index 20c132bd2..a36b268e5 100644 --- a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/action/GroupActionByDateTests.kt +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/action/GroupActionByDateTests.kt @@ -9,14 +9,18 @@ import fr.gouv.gmampa.rapportnav.mocks.mission.action.FishActionControlMock import fr.gouv.gmampa.rapportnav.mocks.mission.action.NavActionStatusMock import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest import java.time.LocalDate import java.time.LocalDateTime import java.time.ZoneOffset import java.time.ZonedDateTime +@SpringBootTest(classes = [GroupActionByDate::class]) class GroupActionByDateTest { - private val groupActionByDate = GroupActionByDate() + @Autowired + private lateinit var groupActionByDate: GroupActionByDate @Test fun `execute should return null when actions is null`() { diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionFreeNoteMock.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionFreeNoteMock.kt new file mode 100644 index 000000000..9543d9fa7 --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionFreeNoteMock.kt @@ -0,0 +1,21 @@ +package fr.gouv.gmampa.rapportnav.mocks.mission.action + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionFreeNoteEntity +import java.time.LocalDateTime +import java.time.ZoneOffset +import java.time.ZonedDateTime +import java.util.* + +object NavActionFreeNoteMock { + fun create( + startDateTimeUtc: ZonedDateTime = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 12, 6), ZoneOffset.UTC), + observations: String = "Largué, appareillé" + ): ActionFreeNoteEntity { + return ActionFreeNoteEntity( + id = UUID.randomUUID(), + missionId = 1, + startDateTimeUtc = startDateTimeUtc, + observations = observations, + ) + } +} From d2b5cf19a9a2de9080b23eec84b4926ded6589d6 Mon Sep 17 00:00:00 2001 From: lwih Date: Wed, 6 Mar 2024 11:12:34 +0100 Subject: [PATCH 16/19] Export mission - formatted text for timeline --- .../mission/nav/status/ActionStatusType.kt | 10 ++ .../mission/{ => export}/ExportMission.kt | 45 ++----- .../mission/export/FormatActionToString.kt | 62 +++++++++ .../infrastructure/bff/MissionController.kt | 1 + .../use_cases/mission/ExportMissionTest.kt | 29 ++++ .../mission/action/GroupActionByDateTests.kt | 2 +- .../export/FormatActionToStringTests.kt | 125 ++++++++++++++++++ .../mission/action/EnvActionControlMock.kt | 8 +- .../mission/action/NavActionControlMock.kt | 25 ++++ .../mission/action/NavActionStatusMock.kt | 19 ++- 10 files changed, 278 insertions(+), 48 deletions(-) rename backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/{ => export}/ExportMission.kt (82%) create mode 100644 backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/FormatActionToString.kt create mode 100644 backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/ExportMissionTest.kt create mode 100644 backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/FormatActionToStringTests.kt create mode 100644 backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionControlMock.kt diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/nav/status/ActionStatusType.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/nav/status/ActionStatusType.kt index 563184b81..3e7e90c74 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/nav/status/ActionStatusType.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/entities/mission/nav/status/ActionStatusType.kt @@ -19,3 +19,13 @@ fun mapStringToActionStatusType(value: String): ActionStatusType { else -> ActionStatusType.UNKNOWN } } + +fun mapActionStatusTypeToHumanString(value: ActionStatusType): String { + return when (value) { + ActionStatusType.NAVIGATING -> "Navigation" + ActionStatusType.ANCHORED -> "Mouillage" + ActionStatusType.DOCKED -> "Présence à quai" + ActionStatusType.UNAVAILABLE -> "Indisponibilité" + else -> "Inconnu" + } +} diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/ExportMission.kt similarity index 82% rename from backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt rename to backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/ExportMission.kt index 7118d0c1a..b8cbd9e19 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/ExportMission.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/ExportMission.kt @@ -1,11 +1,10 @@ -package fr.gouv.dgampa.rapportnav.domain.use_cases.mission +package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export import fr.gouv.dgampa.rapportnav.config.UseCase import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionActionEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.EnvActionControlEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.MissionAction -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionFreeNoteEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionType import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.NavActionEntity @@ -17,6 +16,7 @@ import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatus import fr.gouv.dgampa.rapportnav.domain.repositories.mission.ExportParams import fr.gouv.dgampa.rapportnav.domain.repositories.mission.IRpnExportRepository import fr.gouv.dgampa.rapportnav.domain.repositories.mission.action.INavActionStatusRepository +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.GetMissionById import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.action.GroupActionByDate import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.crew.GetAgentsCrewByMissionId import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.generalInfo.GetMissionGeneralInfoByMissionId @@ -32,6 +32,7 @@ class ExportMission( private val navActionStatus: INavActionStatusRepository, private val getStatusDurations: GetStatusDurations, private val groupActionByDate: GroupActionByDate, + private val formatActionToString: FormatActionToString, ) { private inline fun List.findDuration(predicate: (GetStatusDurations.ActionStatusWithDuration) -> Boolean): Int { @@ -78,35 +79,6 @@ class ExportMission( } - fun formatEnvControlForTimeline(action: EnvActionControlEntity): String { - // Code to format EnvActionControlEntity - return "EnvControlForTimeline" - } - - fun formatFishControlForTimeline(action: MissionAction): String { - // Code to format MissionActionEntity (Fish) - return "FishControlForTimeline" - } - - fun formatNavNoteForTimeline(action: ActionFreeNoteEntity?): String? { - return action?.let { - val startTime = action.startDateTimeUtc.toLocalTime().toString().padStart(5, '0') - val observation = action.observations ?: "" - return "$startTime - $observation" - } - } - - - fun formatNavStatusForTimeline(action: NavActionEntity): String { - // Code to format NavActionEntity with ActionType.STATUS - return "NavStatusForTimeline" - } - - fun formatNavControlForTimeline(action: NavActionEntity): String { - // Code to format NavActionEntity with ActionType.CONTROL - return "NavControlForTimeline" - } - fun formatActionsForTimeline(actions: List?): Map>? { if (actions.isNullOrEmpty()) { @@ -121,29 +93,28 @@ class ExportMission( when (action) { is MissionActionEntity.EnvAction -> { if (action.envAction?.controlAction?.action is EnvActionControlEntity) { - formatEnvControlForTimeline(action.envAction.controlAction.action) + formatActionToString.formatEnvControl(action.envAction.controlAction.action) } else null } is MissionActionEntity.FishAction -> { if (action.fishAction.controlAction?.action is MissionAction) { - formatFishControlForTimeline(action.fishAction.controlAction.action) + formatActionToString.formatFishControl(action.fishAction.controlAction.action) } else null } is MissionActionEntity.NavAction -> { val navAction: NavActionEntity = action.navAction when (navAction.actionType) { - ActionType.NOTE -> formatNavNoteForTimeline(navAction.freeNoteAction) - ActionType.STATUS -> formatNavStatusForTimeline(navAction) - ActionType.CONTROL -> formatNavControlForTimeline(navAction) + ActionType.NOTE -> formatActionToString.formatNavNote(navAction.freeNoteAction) + ActionType.STATUS -> formatActionToString.formatNavStatus(navAction.statusAction) + ActionType.CONTROL -> formatActionToString.formatNavControl(navAction.controlAction) else -> null } } } } } - return formattedActions } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/FormatActionToString.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/FormatActionToString.kt new file mode 100644 index 000000000..46ba4f06f --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/FormatActionToString.kt @@ -0,0 +1,62 @@ +package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export + +import fr.gouv.dgampa.rapportnav.config.UseCase +import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.EnvActionControlEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.MissionAction +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionControlEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionFreeNoteEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.mapActionStatusTypeToHumanString +import java.time.ZonedDateTime + +@UseCase +class FormatActionToString() { + + fun formatTime(dateTime: ZonedDateTime?): String? { + return dateTime?.let { it.toLocalTime().toString().padStart(5, '0') } ?: "N/A" + } + + + fun formatEnvControl(action: EnvActionControlEntity?): String? { + return action?.let { + val startTime = formatTime(action.actionStartDateTimeUtc) + val endTime = formatTime(action.actionEndDateTimeUtc) + val facade = action.facade?.let { " - $it" } ?: "" + val themes = action.themes?.let { " - ${it.map { theme -> theme.theme }.joinToString(" + ")}" } ?: "" + val amountOfControls = action.actionNumberOfControls?.let { " - $it contrôles" } ?: "" + return "$startTime / $endTime - Contrôle Environnement$facade$themes$amountOfControls" + } + } + + fun formatFishControl(action: MissionAction): String { + // Code to format MissionActionEntity (Fish) + return "FishControl" + } + + fun formatNavNote(action: ActionFreeNoteEntity?): String? { + return action?.let { + val startTime = formatTime(action.startDateTimeUtc) + val observation = action.observations ?: "" + return "$startTime - $observation" + } + } + + + fun formatNavStatus(action: ActionStatusEntity?): String? { + return action?.let { + val startTime = formatTime(action.startDateTimeUtc) + val status = mapActionStatusTypeToHumanString(action.status) + val observation = action.observations?.let { "- $it" } ?: "" + return "$startTime - $status - début $observation" + } + } + + fun formatNavControl(action: ActionControlEntity?): String? { + return action?.let { + val startTime = formatTime(action.startDateTimeUtc) + val endTime = formatTime(action.endDateTimeUtc) + val vesselIdentifier = action.vesselIdentifier?.let { "- $it" } ?: "" + return "$startTime / $endTime - Contrôle administratif $vesselIdentifier" + } + } +} diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/MissionController.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/MissionController.kt index 2adebccfd..07931ede0 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/MissionController.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/infrastructure/bff/MissionController.kt @@ -3,6 +3,7 @@ package fr.gouv.dgampa.rapportnav.infrastructure.bff import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionActionEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionExportEntity import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.* +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.ExportMission import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.generalInfo.AddOrUpdateMissionGeneralInfo import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.generalInfo.GetMissionGeneralInfoByMissionId import fr.gouv.dgampa.rapportnav.domain.use_cases.user.GetControlUnitsForUser diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/ExportMissionTest.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/ExportMissionTest.kt new file mode 100644 index 000000000..cb18badaa --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/ExportMissionTest.kt @@ -0,0 +1,29 @@ +package fr.gouv.gmampa.rapportnav.domain.use_cases.mission + +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.ExportMission +import org.springframework.boot.test.context.SpringBootTest + +@SpringBootTest(classes = [ExportMission::class]) +class ExportMissionTest { + +// @Autowired +// private lateinit var exportMission: ExportMission + +// @MockBean +// private lateinit var rpnExportRepository: IRpnExportRepository + +// @Test +// fun `formatNavNoteForTimeline should return formatted string`() { +//// given(this.rpnExportRepository.exportOdt(missionId = 1)).willReturn(emptyList()) +// val action: ActionFreeNoteEntity = NavActionFreeNoteMock.create() +// assertThat(formatNavNoteForTimeline(action)).isEqualTo("12:06 - Largué, appareillé") +// } +// +// @Test +// fun `formatNavNoteForTimeline should return null if freeNoteAction is null`() { +// val action = null +// assertThat(formatNavNoteForTimeline(action)).isEqualTo("null") +// } + + +} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/action/GroupActionByDateTests.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/action/GroupActionByDateTests.kt index a36b268e5..819fb7e94 100644 --- a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/action/GroupActionByDateTests.kt +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/action/GroupActionByDateTests.kt @@ -54,7 +54,7 @@ class GroupActionByDateTest { ) val action3 = MissionActionEntity.NavAction( - NavActionStatusMock.create() + NavActionStatusMock.createActionStatusEntity().toNavAction() ) val actions = listOf(action1, action2, action3) diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/FormatActionToStringTests.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/FormatActionToStringTests.kt new file mode 100644 index 000000000..9388ceeb4 --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/FormatActionToStringTests.kt @@ -0,0 +1,125 @@ +package fr.gouv.gmampa.rapportnav.domain.use_cases.mission.export + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.EnvActionControlEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.ThemeEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionControlEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionFreeNoteEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.FormatActionToString +import fr.gouv.gmampa.rapportnav.mocks.mission.action.EnvActionControlMock +import fr.gouv.gmampa.rapportnav.mocks.mission.action.NavActionControlMock +import fr.gouv.gmampa.rapportnav.mocks.mission.action.NavActionFreeNoteMock +import fr.gouv.gmampa.rapportnav.mocks.mission.action.NavActionStatusMock +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import java.time.LocalDateTime +import java.time.ZoneOffset +import java.time.ZonedDateTime + +@SpringBootTest(classes = [FormatActionToString::class]) +class FormatActionToStringTests { + + @Autowired + private lateinit var formatActionToString: FormatActionToString + + @Test + fun `formatTime should return formatted time`() { + assertThat( + formatActionToString.formatTime( + ZonedDateTime.of( + LocalDateTime.of(2022, 1, 2, 12, 0), + ZoneOffset.UTC + ) + ) + ).isEqualTo("12:00") + } + + @Test + fun `formatTime should return NA when null`() { + assertThat(formatActionToString.formatTime(null)).isEqualTo("N/A") + } + + @Test + fun `formatEnvControl should return basic formatted string`() { + val action: EnvActionControlEntity = EnvActionControlMock.create() + assertThat(formatActionToString.formatEnvControl(action)).isEqualTo("12:00 / 14:00 - Contrôle Environnement") + } + + @Test + fun `formatEnvControl should return formatted string with facade`() { + val action: EnvActionControlEntity = EnvActionControlMock.create(facade = "Golfe de Gascogne") + assertThat(formatActionToString.formatEnvControl(action)).isEqualTo("12:00 / 14:00 - Contrôle Environnement - Golfe de Gascogne") + } + + @Test + fun `formatEnvControl should return formatted string with themes`() { + val themes = listOf( + ThemeEntity( + theme = "Rejets illicites", + ), + ThemeEntity( + theme = "Natura 2000", + ) + ) + val action: EnvActionControlEntity = EnvActionControlMock.create(themes = themes) + assertThat(formatActionToString.formatEnvControl(action)).isEqualTo("12:00 / 14:00 - Contrôle Environnement - Rejets illicites + Natura 2000") + } + + @Test + fun `formatEnvControl should return formatted string with amount of controls`() { + val action: EnvActionControlEntity = EnvActionControlMock.create(actionNumberOfControls = 2) + assertThat(formatActionToString.formatEnvControl(action)).isEqualTo("12:00 / 14:00 - Contrôle Environnement - 2 contrôles") + } + + @Test + fun `formatEnvControl should return null if action is null`() { + val action = null + assertThat(formatActionToString.formatEnvControl(action)).isNull() + } + + @Test + fun `formatNavNote should return formatted string`() { + val action: ActionFreeNoteEntity = NavActionFreeNoteMock.create() + assertThat(formatActionToString.formatNavNote(action)).isEqualTo("12:06 - Largué, appareillé") + } + + @Test + fun `formatNavNote should return null if action is null`() { + val action = null + assertThat(formatActionToString.formatNavNote(action)).isNull() + } + + @Test + fun `formatNavStatus should return formatted string`() { + val action: ActionStatusEntity = NavActionStatusMock.createActionStatusEntity() + assertThat(formatActionToString.formatNavStatus(action)).isEqualTo("12:00 - Navigation - début - observations") + } + + @Test + fun `formatNavStatus should return formatted string without observations`() { + val action: ActionStatusEntity = NavActionStatusMock.createActionStatusEntity(observations = null) + assertThat(formatActionToString.formatNavStatus(action)).isEqualTo("12:00 - Navigation - début ") + } + + @Test + fun `formatNavStatus should return null if action is null`() { + val action = null + assertThat(formatActionToString.formatNavStatus(action)).isNull() + } + + @Test + fun `formatNavControl should return formatted string`() { + val action: ActionControlEntity = NavActionControlMock.createActionControlEntity() + assertThat(formatActionToString.formatNavControl(action)).isEqualTo("12:00 / 14:00 - Contrôle administratif ") + } + + @Test + fun `formatNavControl should return null if action is null`() { + val action = null + assertThat(formatActionToString.formatNavControl(action)).isNull() + } + + +} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/EnvActionControlMock.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/EnvActionControlMock.kt index 8bfe69109..d79a39730 100644 --- a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/EnvActionControlMock.kt +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/EnvActionControlMock.kt @@ -2,14 +2,16 @@ package fr.gouv.gmampa.rapportnav.mocks.mission.action import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.* import org.locationtech.jts.geom.Geometry +import java.time.LocalDateTime +import java.time.ZoneOffset import java.time.ZonedDateTime import java.util.* object EnvActionControlMock { fun create( id: UUID = UUID.randomUUID(), - actionStartDateTimeUtc: ZonedDateTime? = null, - actionEndDateTimeUtc: ZonedDateTime? = null, + actionStartDateTimeUtc: ZonedDateTime? = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 12, 0), ZoneOffset.UTC), + actionEndDateTimeUtc: ZonedDateTime? = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 14, 0), ZoneOffset.UTC), geom: Geometry? = null, facade: String? = null, department: String? = null, @@ -17,7 +19,7 @@ object EnvActionControlMock { isComplianceWithWaterRegulationsControl: Boolean? = null, isSafetyEquipmentAndStandardsComplianceControl: Boolean? = null, isSeafarersControl: Boolean? = null, - themes: List? = emptyList(), + themes: List? = null, observations: String? = null, actionNumberOfControls: Int? = null, actionTargetType: ActionTargetTypeEnum? = null, diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionControlMock.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionControlMock.kt new file mode 100644 index 000000000..d3b289bab --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionControlMock.kt @@ -0,0 +1,25 @@ +package fr.gouv.gmampa.rapportnav.mocks.mission.action + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionControlEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.control.ControlMethod +import java.time.LocalDateTime +import java.time.ZoneOffset +import java.time.ZonedDateTime +import java.util.* + +object NavActionControlMock { + + fun createActionControlEntity( + startDateTimeUtc: ZonedDateTime = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 12, 0), ZoneOffset.UTC), + observations: String? = "observations" + ): ActionControlEntity { + return ActionControlEntity( + id = UUID.randomUUID(), + missionId = 1, + startDateTimeUtc = startDateTimeUtc, + endDateTimeUtc = startDateTimeUtc.plusHours(2), + controlMethod = ControlMethod.SEA, + observations = observations + ) + } +} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionStatusMock.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionStatusMock.kt index 1e9f9a416..61140f36a 100644 --- a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionStatusMock.kt +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionStatusMock.kt @@ -1,20 +1,25 @@ package fr.gouv.gmampa.rapportnav.mocks.mission.action -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionType -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.NavActionEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusType import java.time.LocalDateTime import java.time.ZoneOffset import java.time.ZonedDateTime import java.util.* object NavActionStatusMock { - fun create(): NavActionEntity { - return NavActionEntity( + + fun createActionStatusEntity( + status: ActionStatusType = ActionStatusType.NAVIGATING, + startDateTimeUtc: ZonedDateTime = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 12, 0), ZoneOffset.UTC), + observations: String? = "observations" + ): ActionStatusEntity { + return ActionStatusEntity( id = UUID.randomUUID(), missionId = 1, - startDateTimeUtc = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 12, 0), ZoneOffset.UTC), - endDateTimeUtc = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 14, 0), ZoneOffset.UTC), - actionType = ActionType.STATUS, + startDateTimeUtc = startDateTimeUtc, + status = status, + observations = observations ) } } From dd8d9922532b3eaf1067fcffd0be3d71d24e113c Mon Sep 17 00:00:00 2001 From: lwih Date: Wed, 6 Mar 2024 17:18:34 +0100 Subject: [PATCH 17/19] Export mission - formatted text for timeline --- .../mission/export/FormatActionToString.kt | 38 +++++++++- .../export/FormatActionToStringTests.kt | 71 +++++++++++++++++-- .../mission/action/FishActionControlMock.kt | 23 ++++-- 3 files changed, 121 insertions(+), 11 deletions(-) diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/FormatActionToString.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/FormatActionToString.kt index 46ba4f06f..695c28757 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/FormatActionToString.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/FormatActionToString.kt @@ -2,6 +2,7 @@ package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export import fr.gouv.dgampa.rapportnav.config.UseCase import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.EnvActionControlEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.InfractionType import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.MissionAction import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionControlEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionFreeNoteEntity @@ -28,9 +29,40 @@ class FormatActionToString() { } } - fun formatFishControl(action: MissionAction): String { - // Code to format MissionActionEntity (Fish) - return "FishControl" + fun formatFishControl(action: MissionAction?): String? { + return action?.let { + val startTime = formatTime(action.actionDatetimeUtc) + val coords = "${action.latitude ?: "N/A"}/${action.longitude ?: "N/A"}" + val vesselInfo = "${action.vesselName ?: "N/A"} - ${action.vesselId}" + val seizureAndDiversion = action.seizureAndDiversion?.let { " - retour du navire au port" } ?: "" + val natinfs: String = listOf( + action.gearInfractions.map { it.natinf.toString() }, + action.logbookInfractions.map { it.natinf.toString() }, + action.speciesInfractions.map { it.natinf.toString() }, + action.otherInfractions.map { it.natinf.toString() } + ).flatten().distinct().let { list -> + if (list.isEmpty()) { + " - RAS" + } else { + " - NATINF: ${list.joinToString(" + ")}" + } + } + val pvCount = listOf( + action.gearInfractions.map { it.infractionType }, + action.logbookInfractions.map { it.infractionType }, + action.speciesInfractions.map { it.infractionType }, + action.otherInfractions.map { it.infractionType } + ).flatten().count { it == InfractionType.WITH_RECORD } + val pv = if (pvCount > 0) "avec PV" else "sans PV" + + val species = if (action.speciesOnboard.isNotEmpty()) { + "Espèces contrôlées: " + action.speciesOnboard.joinToString(" - ") { "${it.speciesCode}: ${it.controlledWeight ?: "N/A"}/${it.declaredWeight ?: "N/A"} kg" } + } else { + "Espèces contrôlées: N/A" + } + + return "$startTime - Contrôle Pêche - $coords - $vesselInfo - $species - Infractions: $pv$natinfs$seizureAndDiversion" + } } fun formatNavNote(action: ActionFreeNoteEntity?): String? { diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/FormatActionToStringTests.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/FormatActionToStringTests.kt index 9388ceeb4..be3301a73 100644 --- a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/FormatActionToStringTests.kt +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/FormatActionToStringTests.kt @@ -2,14 +2,15 @@ package fr.gouv.gmampa.rapportnav.domain.use_cases.mission.export import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.EnvActionControlEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.ThemeEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.InfractionType +import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.MissionAction +import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.SpeciesControl +import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.SpeciesInfraction import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionControlEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionFreeNoteEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.FormatActionToString -import fr.gouv.gmampa.rapportnav.mocks.mission.action.EnvActionControlMock -import fr.gouv.gmampa.rapportnav.mocks.mission.action.NavActionControlMock -import fr.gouv.gmampa.rapportnav.mocks.mission.action.NavActionFreeNoteMock -import fr.gouv.gmampa.rapportnav.mocks.mission.action.NavActionStatusMock +import fr.gouv.gmampa.rapportnav.mocks.mission.action.* import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired @@ -73,12 +74,74 @@ class FormatActionToStringTests { assertThat(formatActionToString.formatEnvControl(action)).isEqualTo("12:00 / 14:00 - Contrôle Environnement - 2 contrôles") } + @Test + fun `formatEnvControl should return the complete formatted string`() { + val action: EnvActionControlEntity = EnvActionControlMock.create( + actionNumberOfControls = 2, + facade = "Golfe de Gascogne", + themes = listOf( + ThemeEntity( + theme = "Rejets illicites", + ) + ) + ) + assertThat(formatActionToString.formatEnvControl(action)).isEqualTo("12:00 / 14:00 - Contrôle Environnement - Golfe de Gascogne - Rejets illicites - 2 contrôles") + } + @Test fun `formatEnvControl should return null if action is null`() { val action = null assertThat(formatActionToString.formatEnvControl(action)).isNull() } + @Test + fun `formatFishControl should return the formatted string`() { + val action: MissionAction = FishActionControlMock.create() + assertThat(formatActionToString.formatFishControl(action)).isEqualTo("12:00 - Contrôle Pêche - 52.14/14.3 - Le Pi - 314 - Espèces contrôlées: N/A - Infractions: sans PV - RAS") + } + + @Test + fun `formatFishControl should return the formatted string with natinfs`() { + val action: MissionAction = FishActionControlMock.create( + speciesInfractions = listOf( + SpeciesInfraction().apply { + infractionType = InfractionType.WITH_RECORD + natinf = 123 + }, + SpeciesInfraction().apply { + infractionType = InfractionType.WITHOUT_RECORD + natinf = 456 + } + ) + ) + assertThat(formatActionToString.formatFishControl(action)).isEqualTo("12:00 - Contrôle Pêche - 52.14/14.3 - Le Pi - 314 - Espèces contrôlées: N/A - Infractions: avec PV - NATINF: 123 + 456") + } + + @Test + fun `formatFishControl should return the complete formatted string with species`() { + val action: MissionAction = FishActionControlMock.create( + speciesOnboard = listOf( + SpeciesControl().apply { + speciesCode = "COD" + declaredWeight = 12.2 + controlledWeight = null + }, + SpeciesControl().apply { + speciesCode = "SOL" + declaredWeight = 12.2 + controlledWeight = 14.2 + } + ) + ) + assertThat(formatActionToString.formatFishControl(action)).isEqualTo("12:00 - Contrôle Pêche - 52.14/14.3 - Le Pi - 314 - Espèces contrôlées: COD: N/A/12.2 kg - SOL: 14.2/12.2 kg - Infractions: sans PV - RAS") + } + + @Test + fun `formatFishControl should return null if action is null`() { + val action = null + assertThat(formatActionToString.formatFishControl(action)).isNull() + } + @Test fun `formatNavNote should return formatted string`() { val action: ActionFreeNoteEntity = NavActionFreeNoteMock.create() diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/FishActionControlMock.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/FishActionControlMock.kt index 962967fc4..19b244d82 100644 --- a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/FishActionControlMock.kt +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/FishActionControlMock.kt @@ -2,17 +2,27 @@ package fr.gouv.gmampa.rapportnav.mocks.mission.action import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.MissionAction import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.MissionActionType +import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.SpeciesControl +import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.SpeciesInfraction +import java.time.LocalDateTime +import java.time.ZoneOffset import java.time.ZonedDateTime import java.util.* object FishActionControlMock { fun create( - missionId: Int, - actionDatetimeUtc: ZonedDateTime, + missionId: Int = 1, + actionDatetimeUtc: ZonedDateTime = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 12, 0), ZoneOffset.UTC), actionType: MissionActionType = MissionActionType.SEA_CONTROL, isDeleted: Boolean = false, hasSomeGearsSeized: Boolean = false, hasSomeSpeciesSeized: Boolean = false, + latitude: Double? = 52.14, + longitude: Double? = 14.3, + vesselId: Int? = 314, + vesselName: String? = "Le Pi", + speciesOnboard: List = listOf(), + speciesInfractions: List = listOf(), ): MissionAction { return MissionAction( id = UUID.randomUUID().hashCode(), @@ -21,8 +31,13 @@ object FishActionControlMock { actionType = actionType, isDeleted = isDeleted, hasSomeGearsSeized = hasSomeGearsSeized, - hasSomeSpeciesSeized = hasSomeSpeciesSeized - // Set other properties to their default values or mocks as needed + hasSomeSpeciesSeized = hasSomeSpeciesSeized, + latitude = latitude, + longitude = longitude, + vesselId = vesselId, + vesselName = vesselName, + speciesOnboard = speciesOnboard, + speciesInfractions = speciesInfractions ) } } From af4791754909ecc536f58975a8a56b0ee1663cae Mon Sep 17 00:00:00 2001 From: lwih Date: Thu, 7 Mar 2024 18:53:34 +0100 Subject: [PATCH 18/19] Export mission - refactor and test --- .../use_cases/mission/export/ExportMission.kt | 126 +++------------- ...oString.kt => FormatActionsForTimeline.kt} | 60 +++++++- .../mission/export/MapStatusDurations.kt | 57 +++++++ .../use_cases/mission/ExportMissionTest.kt | 29 ---- .../mission/export/ExportMissionTests.kt | 117 +++++++++++++++ ...ts.kt => FormatActionsForTimelineTests.kt} | 116 ++++++++++---- .../mission/export/MapStatusDurationsTests.kt | 142 ++++++++++++++++++ .../mocks/mission/NavMissionMock.kt | 56 +++++++ .../mission/action/NavActionStatusMock.kt | 3 + 9 files changed, 537 insertions(+), 169 deletions(-) rename backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/{FormatActionToString.kt => FormatActionsForTimeline.kt} (66%) create mode 100644 backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/MapStatusDurations.kt delete mode 100644 backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/ExportMissionTest.kt create mode 100644 backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/ExportMissionTests.kt rename backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/{FormatActionToStringTests.kt => FormatActionsForTimelineTests.kt} (50%) create mode 100644 backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/MapStatusDurationsTests.kt create mode 100644 backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/NavMissionMock.kt diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/ExportMission.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/ExportMission.kt index b8cbd9e19..c82518056 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/ExportMission.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/ExportMission.kt @@ -1,27 +1,17 @@ package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export import fr.gouv.dgampa.rapportnav.config.UseCase -import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionActionEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionEntity -import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.EnvActionControlEntity -import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.MissionAction -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionType -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.NavActionEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.crew.MissionCrewEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionExportEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.generalInfo.MissionGeneralInfoEntity -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusReason -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusType import fr.gouv.dgampa.rapportnav.domain.repositories.mission.ExportParams import fr.gouv.dgampa.rapportnav.domain.repositories.mission.IRpnExportRepository import fr.gouv.dgampa.rapportnav.domain.repositories.mission.action.INavActionStatusRepository import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.GetMissionById -import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.action.GroupActionByDate import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.crew.GetAgentsCrewByMissionId import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.generalInfo.GetMissionGeneralInfoByMissionId -import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.status.GetStatusDurations -import java.time.LocalDate +import org.slf4j.LoggerFactory @UseCase class ExportMission( @@ -30,115 +20,31 @@ class ExportMission( private val agentsCrewByMissionId: GetAgentsCrewByMissionId, private val getMissionById: GetMissionById, private val navActionStatus: INavActionStatusRepository, - private val getStatusDurations: GetStatusDurations, - private val groupActionByDate: GroupActionByDate, - private val formatActionToString: FormatActionToString, + private val mapStatusDurations: MapStatusDurations, + private val formatActionsForTimeline: FormatActionsForTimeline, ) { - private inline fun List.findDuration(predicate: (GetStatusDurations.ActionStatusWithDuration) -> Boolean): Int { - return find(predicate)?.value?.toInt() ?: 0 - } - - fun computeDurations(mission: MissionEntity, statuses: List): Map> { - val durations = getStatusDurations.computeActionDurations( - missionStartDateTime = mission.startDateTimeUtc, - missionEndDateTime = mission.endDateTimeUtc, - actions = statuses, - ) - - val atSeaDurations = mapOf( - "navigationEffective" to durations.findDuration { it.status == ActionStatusType.NAVIGATING }, - "mouillage" to durations.findDuration { it.status == ActionStatusType.ANCHORED }, - "total" to 0 - ).toMutableMap() - atSeaDurations["total"] = atSeaDurations.values.sum() - - val dockingDurations = mapOf( - "maintenance" to durations.findDuration { it.reason == ActionStatusReason.MAINTENANCE }, - "meteo" to durations.findDuration { it.reason == ActionStatusReason.WEATHER }, - "representation" to durations.findDuration { it.reason == ActionStatusReason.REPRESENTATION }, - "adminFormation" to durations.findDuration { it.reason == ActionStatusReason.ADMINISTRATION }, - "autre" to durations.findDuration { it.reason == ActionStatusReason.OTHER }, - "contrPol" to durations.findDuration { it.reason == ActionStatusReason.HARBOUR_CONTROL }, - "total" to 0 - ).toMutableMap() - dockingDurations["total"] = dockingDurations.values.sum() - - val unavailabilityDurations = mapOf( - "technique" to durations.findDuration { it.reason == ActionStatusReason.TECHNICAL }, - "personnel" to durations.findDuration { it.reason == ActionStatusReason.PERSONNEL }, - "total" to 0 - ).toMutableMap() - unavailabilityDurations["total"] = unavailabilityDurations.values.sum() - - return mapOf( - "atSeaDurations" to atSeaDurations.toMap(), - "dockingDurations" to dockingDurations.toMap(), - "unavailabilityDurations" to unavailabilityDurations.toMap() - ) - } - - - fun formatActionsForTimeline(actions: List?): Map>? { - - if (actions.isNullOrEmpty()) { - return null - } - // Group actions by date - val groupedActions = groupActionByDate.execute(actions = actions) - - // Map each group to list of formatted strings - val formattedActions = groupedActions?.mapValues { (_, actionsOnDate) -> - actionsOnDate.mapNotNull { action -> - when (action) { - is MissionActionEntity.EnvAction -> { - if (action.envAction?.controlAction?.action is EnvActionControlEntity) { - formatActionToString.formatEnvControl(action.envAction.controlAction.action) - } else null - } - - is MissionActionEntity.FishAction -> { - if (action.fishAction.controlAction?.action is MissionAction) { - formatActionToString.formatFishControl(action.fishAction.controlAction.action) - } else null - } - - is MissionActionEntity.NavAction -> { - val navAction: NavActionEntity = action.navAction - when (navAction.actionType) { - ActionType.NOTE -> formatActionToString.formatNavNote(navAction.freeNoteAction) - ActionType.STATUS -> formatActionToString.formatNavStatus(navAction.statusAction) - ActionType.CONTROL -> formatActionToString.formatNavControl(navAction.controlAction) - else -> null - } - } - } - } - } - return formattedActions - } + private val logger = LoggerFactory.getLogger(ExportMission::class.java) fun exportOdt(missionId: Int): MissionExportEntity? { + try { + val mission: MissionEntity? = getMissionById.execute(missionId = missionId) + if (mission == null) { + logger.error("[exportOdt] Mission not found for missionId: $missionId") + return null + } - val mission: MissionEntity? = getMissionById.execute(missionId = missionId) - - return if (mission == null) { - null - } else { val generalInfo: MissionGeneralInfoEntity? = getMissionGeneralInfoByMissionId.execute(missionId) val agentsCrew: List = agentsCrewByMissionId.execute(missionId = missionId) val statuses = navActionStatus.findAllByMissionId(missionId = missionId).sortedBy { it.startDateTimeUtc } .map { it.toActionStatusEntity() } - - val durations = computeDurations(mission, statuses) - - // TODO quelle formule utiliser? celle ci ou plutot missionEnd - missionStart ? + val durations = mapStatusDurations.execute(mission, statuses) val missionDuration = (durations["atSeaDurations"]?.get("total") ?: 0) + (durations["dockingDurations"]?.get("total") ?: 0) + (durations["unavailabilityDurations"]?.get("total") ?: 0) - val timeline = formatActionsForTimeline(mission.actions) + val timeline = formatActionsForTimeline.formatTimeline(mission.actions) val exportParams = ExportParams( service = mission.openBy, @@ -158,10 +64,12 @@ class ExportMission( crew = agentsCrew, timeline = timeline ) -// return null - exportRepository.exportOdt(exportParams) + return exportRepository.exportOdt(exportParams) + } catch (e: Exception) { + logger.error("[exportOdt] error occurred during exportOdt: ${e.message}") + return null } - } + } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/FormatActionToString.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/FormatActionsForTimeline.kt similarity index 66% rename from backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/FormatActionToString.kt rename to backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/FormatActionsForTimeline.kt index 695c28757..71b9177d4 100644 --- a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/FormatActionToString.kt +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/FormatActionsForTimeline.kt @@ -1,20 +1,70 @@ package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export import fr.gouv.dgampa.rapportnav.config.UseCase +import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionActionEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.EnvActionControlEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.InfractionType import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.MissionAction -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionControlEntity -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionFreeNoteEntity -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.* import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.mapActionStatusTypeToHumanString +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.action.GroupActionByDate +import java.time.LocalDate import java.time.ZonedDateTime @UseCase -class FormatActionToString() { +class FormatActionsForTimeline( + private val groupActionByDate: GroupActionByDate, +) { + + fun formatTimeline(actions: List?): Map>? { + + if (actions.isNullOrEmpty()) { + return null + } + // Group actions by date + val groupedActions = groupActionByDate.execute(actions = actions) + + // Map each group to list of formatted strings + return groupedActions?.mapValues { (_, actionsOnDate) -> + actionsOnDate.mapNotNull { action -> + formatAction(action) + } + } + } + + private fun formatAction(action: MissionActionEntity): String? { + return when (action) { + is MissionActionEntity.EnvAction -> formatEnvAction(action) + is MissionActionEntity.FishAction -> formatFishAction(action) + is MissionActionEntity.NavAction -> formatNavAction(action) + } + } + + private fun formatEnvAction(action: MissionActionEntity.EnvAction): String? { + return action.envAction?.controlAction?.action?.let { envControlAction -> + formatEnvControl(envControlAction) + } + } + + private fun formatFishAction(action: MissionActionEntity.FishAction): String? { + return action.fishAction.controlAction?.action?.let { fishControlAction -> + formatFishControl(fishControlAction) + } + } + + private fun formatNavAction(action: MissionActionEntity.NavAction): String? { + val navAction = action.navAction + return when (navAction.actionType) { + ActionType.NOTE -> formatNavNote(navAction.freeNoteAction) + ActionType.STATUS -> formatNavStatus(navAction.statusAction) + ActionType.CONTROL -> formatNavControl(navAction.controlAction) + else -> null + } + } + fun formatTime(dateTime: ZonedDateTime?): String? { - return dateTime?.let { it.toLocalTime().toString().padStart(5, '0') } ?: "N/A" + return dateTime?.toLocalTime()?.toString()?.padStart(5, '0') ?: "N/A" } diff --git a/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/MapStatusDurations.kt b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/MapStatusDurations.kt new file mode 100644 index 000000000..b675f7640 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/dgampa/rapportnav/domain/use_cases/mission/export/MapStatusDurations.kt @@ -0,0 +1,57 @@ +package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export + +import fr.gouv.dgampa.rapportnav.config.UseCase +import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusReason +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusType +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.status.GetStatusDurations + +@UseCase +class MapStatusDurations( + private val getStatusDurations: GetStatusDurations, +) { + + private inline fun List.findDuration(predicate: (GetStatusDurations.ActionStatusWithDuration) -> Boolean): Int { + return find(predicate)?.value?.toInt() ?: 0 + } + + fun execute(mission: MissionEntity, statuses: List): Map> { + val durations = getStatusDurations.computeActionDurations( + missionStartDateTime = mission.startDateTimeUtc, + missionEndDateTime = mission.endDateTimeUtc, + actions = statuses, + ) + + val atSeaDurations = mapOf( + "navigationEffective" to durations.findDuration { it.status == ActionStatusType.NAVIGATING }, + "mouillage" to durations.findDuration { it.status == ActionStatusType.ANCHORED }, + "total" to 0 + ).toMutableMap() + atSeaDurations["total"] = atSeaDurations.values.sum() + + val dockingDurations = mapOf( + "maintenance" to durations.findDuration { it.reason == ActionStatusReason.MAINTENANCE }, + "meteo" to durations.findDuration { it.reason == ActionStatusReason.WEATHER }, + "representation" to durations.findDuration { it.reason == ActionStatusReason.REPRESENTATION }, + "adminFormation" to durations.findDuration { it.reason == ActionStatusReason.ADMINISTRATION }, + "autre" to durations.findDuration { it.reason == ActionStatusReason.OTHER }, + "contrPol" to durations.findDuration { it.reason == ActionStatusReason.HARBOUR_CONTROL }, + "total" to 0 + ).toMutableMap() + dockingDurations["total"] = dockingDurations.values.sum() + + val unavailabilityDurations = mapOf( + "technique" to durations.findDuration { it.reason == ActionStatusReason.TECHNICAL }, + "personnel" to durations.findDuration { it.reason == ActionStatusReason.PERSONNEL }, + "total" to 0 + ).toMutableMap() + unavailabilityDurations["total"] = unavailabilityDurations.values.sum() + + return mapOf( + "atSeaDurations" to atSeaDurations.toMap(), + "dockingDurations" to dockingDurations.toMap(), + "unavailabilityDurations" to unavailabilityDurations.toMap() + ) + } +} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/ExportMissionTest.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/ExportMissionTest.kt deleted file mode 100644 index cb18badaa..000000000 --- a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/ExportMissionTest.kt +++ /dev/null @@ -1,29 +0,0 @@ -package fr.gouv.gmampa.rapportnav.domain.use_cases.mission - -import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.ExportMission -import org.springframework.boot.test.context.SpringBootTest - -@SpringBootTest(classes = [ExportMission::class]) -class ExportMissionTest { - -// @Autowired -// private lateinit var exportMission: ExportMission - -// @MockBean -// private lateinit var rpnExportRepository: IRpnExportRepository - -// @Test -// fun `formatNavNoteForTimeline should return formatted string`() { -//// given(this.rpnExportRepository.exportOdt(missionId = 1)).willReturn(emptyList()) -// val action: ActionFreeNoteEntity = NavActionFreeNoteMock.create() -// assertThat(formatNavNoteForTimeline(action)).isEqualTo("12:06 - Largué, appareillé") -// } -// -// @Test -// fun `formatNavNoteForTimeline should return null if freeNoteAction is null`() { -// val action = null -// assertThat(formatNavNoteForTimeline(action)).isEqualTo("null") -// } - - -} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/ExportMissionTests.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/ExportMissionTests.kt new file mode 100644 index 000000000..5e4df7898 --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/ExportMissionTests.kt @@ -0,0 +1,117 @@ +package fr.gouv.gmampa.rapportnav.domain.use_cases.mission.export + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionExportEntity +import fr.gouv.dgampa.rapportnav.domain.repositories.mission.ExportParams +import fr.gouv.dgampa.rapportnav.domain.repositories.mission.IRpnExportRepository +import fr.gouv.dgampa.rapportnav.domain.repositories.mission.action.INavActionStatusRepository +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.GetMissionById +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.crew.GetAgentsCrewByMissionId +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.ExportMission +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.FormatActionsForTimeline +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.MapStatusDurations +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.generalInfo.GetMissionGeneralInfoByMissionId +import fr.gouv.gmampa.rapportnav.mocks.mission.NavMissionMock +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertDoesNotThrow +import org.mockito.Mockito.reset +import org.mockito.Mockito.`when` +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.MockBean +import java.time.LocalDateTime +import java.time.ZoneOffset +import java.time.ZonedDateTime + +@SpringBootTest(classes = [ExportMission::class]) +class ExportMissionTests { + + @Autowired + private lateinit var exportMission: ExportMission + + @MockBean + private lateinit var exportRepository: IRpnExportRepository + + @MockBean + private lateinit var getMissionGeneralInfoByMissionId: GetMissionGeneralInfoByMissionId + + @MockBean + private lateinit var agentsCrewByMissionId: GetAgentsCrewByMissionId + + @MockBean + private lateinit var getMissionById: GetMissionById + + @MockBean + private lateinit var navActionStatus: INavActionStatusRepository + + @MockBean + private lateinit var mapStatusDurations: MapStatusDurations + + @MockBean + private lateinit var formatActionsForTimeline: FormatActionsForTimeline + + @BeforeEach + fun setUp() { + reset(exportRepository) + reset(getMissionById) + } + + @Test + fun `exportOdt should return null if mission is not found`() { + val missionId = 123 + `when`(getMissionById.execute(missionId)).thenReturn(null) + + val result = exportMission.exportOdt(missionId) + + assertThat(result).isNull() + } + + @Test + fun `exportOdt should not throw`() { + val missionId = 123 + assertDoesNotThrow { + exportMission.exportOdt(missionId) + } + } + + @Test + fun `exportOdt should return null when throw`() { + val missionId = 123 + `when`(getMissionById.execute(missionId)).thenThrow() + assertThat(exportMission.exportOdt(missionId)).isNull() + } + + @Test + fun `exportOdt should return MissionExportEntity`() { + val missionId = 123 + val output = MissionExportEntity( + fileName = "rapport.odt", + fileContent = "some content" + ) + // check why mockito.any() did not work, hence the creation of this mock var + val exportParams = ExportParams( + service = null, + id = "pam1", + startDateTime = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 12, 0), ZoneOffset.UTC), + endDateTime = null, + presenceMer = emptyMap(), + presenceQuai = emptyMap(), + indisponibilite = emptyMap(), + nbJoursMer = 0, + dureeMission = 0, + patrouilleEnv = 0, + patrouilleMigrant = 0, + distanceMilles = null, + goMarine = null, + essence = null, + crew = emptyList(), + timeline = emptyMap() + ) + `when`(getMissionById.execute(missionId)).thenReturn(NavMissionMock.create()) + `when`(exportRepository.exportOdt(exportParams)).thenReturn(output) + + val result = exportMission.exportOdt(missionId) + assertThat(result).isEqualTo(output) + } +} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/FormatActionToStringTests.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/FormatActionsForTimelineTests.kt similarity index 50% rename from backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/FormatActionToStringTests.kt rename to backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/FormatActionsForTimelineTests.kt index be3301a73..8c9a0ce7a 100644 --- a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/FormatActionToStringTests.kt +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/FormatActionsForTimelineTests.kt @@ -1,34 +1,98 @@ package fr.gouv.gmampa.rapportnav.domain.use_cases.mission.export +import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionActionEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.EnvActionControlEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.envActions.ThemeEntity import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.InfractionType import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.MissionAction import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.SpeciesControl import fr.gouv.dgampa.rapportnav.domain.entities.mission.fish.fishActions.SpeciesInfraction -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionControlEntity -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionFreeNoteEntity -import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity -import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.FormatActionToString +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.* +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.action.GroupActionByDate +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.FormatActionsForTimeline import fr.gouv.gmampa.rapportnav.mocks.mission.action.* import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.mockito.ArgumentMatchers.any +import org.mockito.Mockito import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.MockBean +import java.time.LocalDate import java.time.LocalDateTime import java.time.ZoneOffset import java.time.ZonedDateTime -@SpringBootTest(classes = [FormatActionToString::class]) -class FormatActionToStringTests { +@SpringBootTest(classes = [FormatActionsForTimeline::class]) +class FormatActionsForTimelineTests { @Autowired - private lateinit var formatActionToString: FormatActionToString + private lateinit var formatActionsForTimeline: FormatActionsForTimeline + + @MockBean + private lateinit var groupActionByDate: GroupActionByDate + + private val envControl = + MissionActionEntity.EnvAction(ExtendedEnvActionEntity.fromEnvActionEntity(EnvActionControlMock.create())) + private val fishControl = + MissionActionEntity.FishAction(ExtendedFishActionEntity.fromMissionAction(FishActionControlMock.create())) + private val navControl = + MissionActionEntity.NavAction(NavActionControlMock.createActionControlEntity().toNavAction()) + private val navStatus = MissionActionEntity.NavAction(NavActionStatusMock.createActionStatusEntity().toNavAction()) + private val navFreeNote = MissionActionEntity.NavAction(NavActionFreeNoteMock.create().toNavAction()) + + @BeforeEach + fun setUp() { + // Set up mock behavior for GroupActionByDate + val data: Map> = mapOf( + LocalDate.of(2022, 1, 1) to listOf(envControl, fishControl), + LocalDate.of(2022, 1, 2) to listOf(navControl, navStatus, navFreeNote) + ) + Mockito.`when`(groupActionByDate.execute(any())).thenReturn(data) + } + + @Test + fun `formatTimeline should return null when null given`() { + assertThat(formatActionsForTimeline.formatTimeline(null)).isNull() + } + + @Test + fun `formatTimeline should return null when empty list given`() { + assertThat(formatActionsForTimeline.formatTimeline(emptyList())).isNull() + } + + @Test + fun `formatTimeline should return the correct structure and text`() { + assertThat( + formatActionsForTimeline.formatTimeline( + listOf( + envControl, + fishControl, + navControl, + navStatus, + navFreeNote + ) + ) + ).isEqualTo( + mapOf( + LocalDate.of(2022, 1, 1) to listOf( + "12:00 / 14:00 - Contrôle Environnement", + "12:00 - Contrôle Pêche - 52.14/14.3 - Le Pi - 314 - Espèces contrôlées: N/A - Infractions: sans PV - RAS", + ), + LocalDate.of(2022, 1, 2) to listOf( + "12:00 / 14:00 - Contrôle administratif ", + "12:00 - Navigation - début - observations", + "12:06 - Largué, appareillé" + ) + ) + ) + } @Test fun `formatTime should return formatted time`() { assertThat( - formatActionToString.formatTime( + formatActionsForTimeline.formatTime( ZonedDateTime.of( LocalDateTime.of(2022, 1, 2, 12, 0), ZoneOffset.UTC @@ -39,19 +103,19 @@ class FormatActionToStringTests { @Test fun `formatTime should return NA when null`() { - assertThat(formatActionToString.formatTime(null)).isEqualTo("N/A") + assertThat(formatActionsForTimeline.formatTime(null)).isEqualTo("N/A") } @Test fun `formatEnvControl should return basic formatted string`() { val action: EnvActionControlEntity = EnvActionControlMock.create() - assertThat(formatActionToString.formatEnvControl(action)).isEqualTo("12:00 / 14:00 - Contrôle Environnement") + assertThat(formatActionsForTimeline.formatEnvControl(action)).isEqualTo("12:00 / 14:00 - Contrôle Environnement") } @Test fun `formatEnvControl should return formatted string with facade`() { val action: EnvActionControlEntity = EnvActionControlMock.create(facade = "Golfe de Gascogne") - assertThat(formatActionToString.formatEnvControl(action)).isEqualTo("12:00 / 14:00 - Contrôle Environnement - Golfe de Gascogne") + assertThat(formatActionsForTimeline.formatEnvControl(action)).isEqualTo("12:00 / 14:00 - Contrôle Environnement - Golfe de Gascogne") } @Test @@ -65,13 +129,13 @@ class FormatActionToStringTests { ) ) val action: EnvActionControlEntity = EnvActionControlMock.create(themes = themes) - assertThat(formatActionToString.formatEnvControl(action)).isEqualTo("12:00 / 14:00 - Contrôle Environnement - Rejets illicites + Natura 2000") + assertThat(formatActionsForTimeline.formatEnvControl(action)).isEqualTo("12:00 / 14:00 - Contrôle Environnement - Rejets illicites + Natura 2000") } @Test fun `formatEnvControl should return formatted string with amount of controls`() { val action: EnvActionControlEntity = EnvActionControlMock.create(actionNumberOfControls = 2) - assertThat(formatActionToString.formatEnvControl(action)).isEqualTo("12:00 / 14:00 - Contrôle Environnement - 2 contrôles") + assertThat(formatActionsForTimeline.formatEnvControl(action)).isEqualTo("12:00 / 14:00 - Contrôle Environnement - 2 contrôles") } @Test @@ -85,19 +149,19 @@ class FormatActionToStringTests { ) ) ) - assertThat(formatActionToString.formatEnvControl(action)).isEqualTo("12:00 / 14:00 - Contrôle Environnement - Golfe de Gascogne - Rejets illicites - 2 contrôles") + assertThat(formatActionsForTimeline.formatEnvControl(action)).isEqualTo("12:00 / 14:00 - Contrôle Environnement - Golfe de Gascogne - Rejets illicites - 2 contrôles") } @Test fun `formatEnvControl should return null if action is null`() { val action = null - assertThat(formatActionToString.formatEnvControl(action)).isNull() + assertThat(formatActionsForTimeline.formatEnvControl(action)).isNull() } @Test fun `formatFishControl should return the formatted string`() { val action: MissionAction = FishActionControlMock.create() - assertThat(formatActionToString.formatFishControl(action)).isEqualTo("12:00 - Contrôle Pêche - 52.14/14.3 - Le Pi - 314 - Espèces contrôlées: N/A - Infractions: sans PV - RAS") + assertThat(formatActionsForTimeline.formatFishControl(action)).isEqualTo("12:00 - Contrôle Pêche - 52.14/14.3 - Le Pi - 314 - Espèces contrôlées: N/A - Infractions: sans PV - RAS") } @Test @@ -114,7 +178,7 @@ class FormatActionToStringTests { } ) ) - assertThat(formatActionToString.formatFishControl(action)).isEqualTo("12:00 - Contrôle Pêche - 52.14/14.3 - Le Pi - 314 - Espèces contrôlées: N/A - Infractions: avec PV - NATINF: 123 + 456") + assertThat(formatActionsForTimeline.formatFishControl(action)).isEqualTo("12:00 - Contrôle Pêche - 52.14/14.3 - Le Pi - 314 - Espèces contrôlées: N/A - Infractions: avec PV - NATINF: 123 + 456") } @Test @@ -133,55 +197,55 @@ class FormatActionToStringTests { } ) ) - assertThat(formatActionToString.formatFishControl(action)).isEqualTo("12:00 - Contrôle Pêche - 52.14/14.3 - Le Pi - 314 - Espèces contrôlées: COD: N/A/12.2 kg - SOL: 14.2/12.2 kg - Infractions: sans PV - RAS") + assertThat(formatActionsForTimeline.formatFishControl(action)).isEqualTo("12:00 - Contrôle Pêche - 52.14/14.3 - Le Pi - 314 - Espèces contrôlées: COD: N/A/12.2 kg - SOL: 14.2/12.2 kg - Infractions: sans PV - RAS") } @Test fun `formatFishControl should return null if action is null`() { val action = null - assertThat(formatActionToString.formatFishControl(action)).isNull() + assertThat(formatActionsForTimeline.formatFishControl(action)).isNull() } @Test fun `formatNavNote should return formatted string`() { val action: ActionFreeNoteEntity = NavActionFreeNoteMock.create() - assertThat(formatActionToString.formatNavNote(action)).isEqualTo("12:06 - Largué, appareillé") + assertThat(formatActionsForTimeline.formatNavNote(action)).isEqualTo("12:06 - Largué, appareillé") } @Test fun `formatNavNote should return null if action is null`() { val action = null - assertThat(formatActionToString.formatNavNote(action)).isNull() + assertThat(formatActionsForTimeline.formatNavNote(action)).isNull() } @Test fun `formatNavStatus should return formatted string`() { val action: ActionStatusEntity = NavActionStatusMock.createActionStatusEntity() - assertThat(formatActionToString.formatNavStatus(action)).isEqualTo("12:00 - Navigation - début - observations") + assertThat(formatActionsForTimeline.formatNavStatus(action)).isEqualTo("12:00 - Navigation - début - observations") } @Test fun `formatNavStatus should return formatted string without observations`() { val action: ActionStatusEntity = NavActionStatusMock.createActionStatusEntity(observations = null) - assertThat(formatActionToString.formatNavStatus(action)).isEqualTo("12:00 - Navigation - début ") + assertThat(formatActionsForTimeline.formatNavStatus(action)).isEqualTo("12:00 - Navigation - début ") } @Test fun `formatNavStatus should return null if action is null`() { val action = null - assertThat(formatActionToString.formatNavStatus(action)).isNull() + assertThat(formatActionsForTimeline.formatNavStatus(action)).isNull() } @Test fun `formatNavControl should return formatted string`() { val action: ActionControlEntity = NavActionControlMock.createActionControlEntity() - assertThat(formatActionToString.formatNavControl(action)).isEqualTo("12:00 / 14:00 - Contrôle administratif ") + assertThat(formatActionsForTimeline.formatNavControl(action)).isEqualTo("12:00 / 14:00 - Contrôle administratif ") } @Test fun `formatNavControl should return null if action is null`() { val action = null - assertThat(formatActionToString.formatNavControl(action)).isNull() + assertThat(formatActionsForTimeline.formatNavControl(action)).isNull() } diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/MapStatusDurationsTests.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/MapStatusDurationsTests.kt new file mode 100644 index 000000000..4d27d8964 --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/domain/use_cases/mission/export/MapStatusDurationsTests.kt @@ -0,0 +1,142 @@ +package fr.gouv.gmampa.rapportnav.domain.use_cases.mission.export + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusReason +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusType +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.MapStatusDurations +import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.status.GetStatusDurations +import fr.gouv.gmampa.rapportnav.mocks.mission.NavMissionMock +import fr.gouv.gmampa.rapportnav.mocks.mission.action.NavActionStatusMock +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.mockito.Mockito +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.MockBean +import java.time.LocalDateTime +import java.time.ZoneOffset +import java.time.ZonedDateTime + +@SpringBootTest(classes = [MapStatusDurations::class]) +class MapStatusDurationsTests { + + @Autowired + private lateinit var mapStatusDurations: MapStatusDurations + + @MockBean + private lateinit var getStatusDurations: GetStatusDurations + + @Test + fun `formatTimeline should return null when null given`() { + val inputMission = NavMissionMock.create( + startDateTimeUtc = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 12, 0), ZoneOffset.UTC), + endDateTimeUtc = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 22, 0), ZoneOffset.UTC), + ) + val inputStatuses = listOf( + NavActionStatusMock.createActionStatusEntity( + status = ActionStatusType.ANCHORED, + startDateTimeUtc = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 12, 0), ZoneOffset.UTC) + ), + NavActionStatusMock.createActionStatusEntity( + status = ActionStatusType.DOCKED, + reason = ActionStatusReason.WEATHER, + startDateTimeUtc = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 14, 0), ZoneOffset.UTC) + ), + NavActionStatusMock.createActionStatusEntity( + status = ActionStatusType.DOCKED, + reason = ActionStatusReason.REPRESENTATION, + startDateTimeUtc = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 16, 0), ZoneOffset.UTC) + ), + NavActionStatusMock.createActionStatusEntity( + status = ActionStatusType.NAVIGATING, + startDateTimeUtc = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 18, 0), ZoneOffset.UTC) + ), + NavActionStatusMock.createActionStatusEntity( + status = ActionStatusType.UNAVAILABLE, + reason = ActionStatusReason.PERSONNEL, + startDateTimeUtc = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 20, 0), ZoneOffset.UTC) + ) + ) + val mockData = listOf( + GetStatusDurations.ActionStatusWithDuration(status = ActionStatusType.ANCHORED, value = 120, reason = null), + GetStatusDurations.ActionStatusWithDuration( + status = ActionStatusType.DOCKED, + value = 0, + reason = ActionStatusReason.MAINTENANCE + ), + GetStatusDurations.ActionStatusWithDuration( + status = ActionStatusType.DOCKED, + value = 120, + reason = ActionStatusReason.WEATHER + ), + GetStatusDurations.ActionStatusWithDuration( + status = ActionStatusType.DOCKED, + value = 120, + reason = ActionStatusReason.REPRESENTATION + ), + GetStatusDurations.ActionStatusWithDuration( + status = ActionStatusType.DOCKED, + value = 0, + reason = ActionStatusReason.ADMINISTRATION + ), + GetStatusDurations.ActionStatusWithDuration( + status = ActionStatusType.DOCKED, + value = 0, + reason = ActionStatusReason.HARBOUR_CONTROL + ), + GetStatusDurations.ActionStatusWithDuration( + status = ActionStatusType.DOCKED, + value = 0, + reason = ActionStatusReason.OTHER + ), + GetStatusDurations.ActionStatusWithDuration( + status = ActionStatusType.NAVIGATING, + value = 120, + reason = null + ), + GetStatusDurations.ActionStatusWithDuration( + status = ActionStatusType.UNAVAILABLE, + value = 0, + reason = ActionStatusReason.TECHNICAL + ), + GetStatusDurations.ActionStatusWithDuration( + status = ActionStatusType.UNAVAILABLE, + value = 120, + reason = ActionStatusReason.PERSONNEL + ), + GetStatusDurations.ActionStatusWithDuration( + status = ActionStatusType.UNAVAILABLE, + value = 0, + reason = ActionStatusReason.OTHER + ) + ) + Mockito.`when`( + getStatusDurations.computeActionDurations( + missionStartDateTime = inputMission.startDateTimeUtc, + missionEndDateTime = inputMission.endDateTimeUtc, + actions = inputStatuses + ) + ).thenReturn(mockData) + assertThat(mapStatusDurations.execute(inputMission, inputStatuses)).isEqualTo( + mapOf( + "atSeaDurations" to mapOf( + "mouillage" to 120, "navigationEffective" to 120, "total" to 240 + ), + "dockingDurations" to mapOf( + "adminFormation" to 0, + "autre" to 0, + "contrPol" to 0, + "maintenance" to 0, + "meteo" to 120, + "representation" to 120, + "total" to 240 + ), + "unavailabilityDurations" to mapOf( + "personnel" to 120, "technique" to 0, "total" to 120 + ) + ) + ) + + } + + +} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/NavMissionMock.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/NavMissionMock.kt new file mode 100644 index 000000000..b7283b6b3 --- /dev/null +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/NavMissionMock.kt @@ -0,0 +1,56 @@ +package fr.gouv.gmampa.rapportnav.mocks.mission + +import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionActionEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.MissionSourceEnum +import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.MissionTypeEnum +import fr.gouv.dgampa.rapportnav.domain.entities.mission.env.controlResources.LegacyControlUnitEntity +import org.locationtech.jts.geom.MultiPolygon +import java.time.LocalDateTime +import java.time.ZoneOffset +import java.time.ZonedDateTime + +object NavMissionMock { + fun create( + id: Int = 1, + missionTypes: List = listOf(), + controlUnits: List = listOf(), + openBy: String? = null, + closedBy: String? = null, + observationsCacem: String? = null, + observationsCnsp: String? = null, + facade: String? = null, + geom: MultiPolygon? = null, + startDateTimeUtc: ZonedDateTime = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 12, 0), ZoneOffset.UTC), + endDateTimeUtc: ZonedDateTime? = null, + isClosed: Boolean = false, + isDeleted: Boolean = false, + isGeometryComputedFromControls: Boolean = false, + missionSource: MissionSourceEnum = MissionSourceEnum.MONITORENV, + hasMissionOrder: Boolean = false, + isUnderJdp: Boolean = false, + actions: List? = null, + ): MissionEntity { + return MissionEntity( + id = id, + missionTypes = missionTypes, + controlUnits = controlUnits, + openBy = openBy, + closedBy = closedBy, + observationsCacem = observationsCacem, + observationsCnsp = observationsCnsp, + facade = facade, + geom = geom, + startDateTimeUtc = startDateTimeUtc, + endDateTimeUtc = endDateTimeUtc, + isClosed = isClosed, + isDeleted = isDeleted, + isGeometryComputedFromControls = isGeometryComputedFromControls, + missionSource = missionSource, + hasMissionOrder = hasMissionOrder, + isUnderJdp = isUnderJdp, + actions = actions + ) + } + +} diff --git a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionStatusMock.kt b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionStatusMock.kt index 61140f36a..82a446164 100644 --- a/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionStatusMock.kt +++ b/backend/src/test/kotlin/fr/gouv/gmampa/rapportnav/mocks/mission/action/NavActionStatusMock.kt @@ -1,6 +1,7 @@ package fr.gouv.gmampa.rapportnav.mocks.mission.action import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatusEntity +import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusReason import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.status.ActionStatusType import java.time.LocalDateTime import java.time.ZoneOffset @@ -12,6 +13,7 @@ object NavActionStatusMock { fun createActionStatusEntity( status: ActionStatusType = ActionStatusType.NAVIGATING, startDateTimeUtc: ZonedDateTime = ZonedDateTime.of(LocalDateTime.of(2022, 1, 2, 12, 0), ZoneOffset.UTC), + reason: ActionStatusReason? = null, observations: String? = "observations" ): ActionStatusEntity { return ActionStatusEntity( @@ -19,6 +21,7 @@ object NavActionStatusMock { missionId = 1, startDateTimeUtc = startDateTimeUtc, status = status, + reason = reason, observations = observations ) } From e693ab4691f52569efa0002a73e213cac36503e0 Mon Sep 17 00:00:00 2001 From: lwih Date: Tue, 12 Mar 2024 19:10:40 +0100 Subject: [PATCH 19/19] Release 1.2.0 - Test rapport de patrouille --- .gitlab-ci.yml | 2 +- backend/build.gradle.kts | 2 +- frontend/package-lock.json | 4 ++-- frontend/package.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d6396aef4..c073dfe38 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -15,7 +15,7 @@ variables: value: postgres:15.6-alpine description: "Image de la base de données" PROJECT_VERSION: - value: "1.1.0" + value: "1.2.0" description: "Version du projet à déployer" SERVER_ENV_INT: value: "int-rapportnav-appli01" diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts index c4b440e64..dd68c3947 100644 --- a/backend/build.gradle.kts +++ b/backend/build.gradle.kts @@ -1,7 +1,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile group = "fr.gouv.dgampa" -version = "1.1.0" +version = "1.2.0" description = "RapportNav" val kotlinVersion by extra("1.9.21") diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 58826415d..4ef39e66a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "frontend", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "frontend", - "version": "1.1.0", + "version": "1.2.0", "dependencies": { "@apollo/client": "^3.9.5", "@mtes-mct/monitor-ui": "^13.3.1", diff --git a/frontend/package.json b/frontend/package.json index 888e4c47b..737fbe6e8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "1.1.0", + "version": "1.2.0", "private": true, "type": "module", "engines": {