Skip to content

Commit

Permalink
Merge pull request #109 from MTES-MCT/rapport-fixes
Browse files Browse the repository at this point in the history
Rapport de patrouille - Add nbDaysAtSea - loading on button frontend …
  • Loading branch information
lwih authored Mar 14, 2024
2 parents 468126b + 26006d1 commit b5dd3ad
Show file tree
Hide file tree
Showing 26 changed files with 611 additions and 226 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ data class ActionStatusEntity(
val reason: ActionStatusReason? = null,
val observations: String? = null,
) {
fun toNavAction(): NavActionEntity {
fun toNavActionEntity(): NavActionEntity {
return NavActionEntity(
id = id,
missionId = missionId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ data class MissionCrewEntity(

val id: Int? = null,
val agent: AgentEntity,
val comment: String?,
val comment: String? = null,
val role: AgentRoleEntity,
val missionId: Int,
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ 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 fr.gouv.dgampa.rapportnav.infrastructure.rapportnav1.adapters.inputs.TimelineActions
import java.time.ZonedDateTime

data class ExportParams(
val service: String?,
val id: String,
val startDateTime: String?,
val endDateTime: String?,
val startDateTime: ZonedDateTime?,
val endDateTime: ZonedDateTime?,
val presenceMer: Map<String, Int>,
val presenceQuai: Map<String, Int>,
val indisponibilite: Map<String, Int>,
Expand All @@ -19,7 +21,7 @@ data class ExportParams(
val goMarine: Float?,
val essence: Float?,
val crew: List<MissionCrewEntity>,
val timeline: Map<String, List<String>>?
val timeline: List<TimelineActions>
)

interface IRpnExportRepository {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ class GetEnvMissionById(
facade = "Outre-Mer",
observationsCacem = null,
startDateTimeUtc = ZonedDateTime.parse("2022-02-15T04:50:09Z"),
// endDateTimeUtc = ZonedDateTime.parse("2022-02-23T20:29:03Z"),
endDateTimeUtc = ZonedDateTime.parse("2022-02-23T20:29:03Z"),
isClosed = false,
isDeleted = false,
missionSource = MissionSourceEnum.RAPPORTNAV,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class GetNavMissionById(

val statuses = navStatusRepository.findAllByMissionId(missionId = missionId)
.map { it.toActionStatusEntity() }
.map { it.toNavAction() }
.map { it.toNavActionEntity() }

val notes = navFreeNoteRepository.findAllByMissionId(missionId = missionId)
.map { it.toActionFreeNoteEntity() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class GetNavActionByIdAndMissionId(
}

ActionType.STATUS -> {
statusActionsRepository.findById(id).orElse(null)?.toActionStatusEntity()?.toNavAction()
statusActionsRepository.findById(id).orElse(null)?.toActionStatusEntity()?.toNavActionEntity()
}

ActionType.NOTE -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,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<MissionActionEntity>?): Map<String, List<MissionActionEntity>>? {
fun execute(actions: List<MissionActionEntity>?): Map<LocalDate, List<MissionActionEntity>>? {
return actions?.groupBy { action ->
when (action) {
is MissionActionEntity.EnvAction -> action.envAction?.controlAction?.action?.actionStartDateTimeUtc?.takeIf { true }
?.toLocalDate().toString()
?.toLocalDate()

is MissionActionEntity.FishAction -> action.fishAction.controlAction?.action?.actionDatetimeUtc?.takeIf { true }
?.toLocalDate().toString()
?.toLocalDate()

is MissionActionEntity.NavAction -> action.navAction.startDateTimeUtc.takeIf { true }
?.toLocalDate().toString()
?.toLocalDate()

else -> null // Handle other types of actions, if any
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import fr.gouv.dgampa.rapportnav.domain.repositories.mission.crew.IMissionCrewRe
@UseCase
class GetAgentsCrewByMissionId(private val agentCrewRepository: IMissionCrewRepository) {

fun execute(missionId: Int): List<MissionCrewEntity> {
return agentCrewRepository.findByMissionId(missionId = missionId).map { it.toMissionCrewEntity() }
fun execute(missionId: Int, commentDefaultsToString: Boolean? = false): List<MissionCrewEntity> {
return agentCrewRepository.findByMissionId(missionId = missionId)
.map { it.toMissionCrewEntity(commentDefaultsToString) }
.sortedBy { it.id }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import fr.gouv.dgampa.rapportnav.domain.repositories.mission.action.INavActionSt
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.generalInfo.GetMissionGeneralInfoByMissionId
import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.status.GetNbOfDaysAtSeaFromNavigationStatus
import org.slf4j.LoggerFactory
import java.time.format.DateTimeFormatter
import kotlin.time.DurationUnit

@UseCase
class ExportMission(
Expand All @@ -22,6 +25,7 @@ class ExportMission(
private val navActionStatus: INavActionStatusRepository,
private val mapStatusDurations: MapStatusDurations,
private val formatActionsForTimeline: FormatActionsForTimeline,
private val getNbOfDaysAtSeaFromNavigationStatus: GetNbOfDaysAtSeaFromNavigationStatus,
) {

private val logger = LoggerFactory.getLogger(ExportMission::class.java)
Expand All @@ -30,44 +34,52 @@ class ExportMission(
try {
val mission: MissionEntity? = getMissionById.execute(missionId = missionId)
if (mission == null) {
logger.error("[exportOdt] Mission not found for missionId: $missionId")
logger.error("[RapportDePatrouille] - Mission not found for missionId: $missionId")
return null
}

val generalInfo: MissionGeneralInfoEntity? = getMissionGeneralInfoByMissionId.execute(missionId)
val agentsCrew: List<MissionCrewEntity> = agentsCrewByMissionId.execute(missionId = missionId)
val agentsCrew: List<MissionCrewEntity> =
agentsCrewByMissionId.execute(missionId = missionId, commentDefaultsToString = true)
val statuses = navActionStatus.findAllByMissionId(missionId = missionId).sortedBy { it.startDateTimeUtc }
.map { it.toActionStatusEntity() }

val durations = mapStatusDurations.execute(mission, statuses)
val durations = mapStatusDurations.execute(mission, statuses, DurationUnit.HOURS)
val missionDuration = (durations["atSeaDurations"]?.get("total") ?: 0) +
(durations["dockingDurations"]?.get("total") ?: 0) +
(durations["unavailabilityDurations"]?.get("total") ?: 0)

val nbOfDaysAtSea = getNbOfDaysAtSeaFromNavigationStatus.execute(
missionStartDateTime = mission.startDateTimeUtc,
missionEndDateTime = mission.endDateTimeUtc,
actions = statuses,
durationUnit = DurationUnit.HOURS
)

val timeline = formatActionsForTimeline.formatTimeline(mission.actions)

val exportParams = ExportParams(
service = mission.openBy,
id = "pam" + mission.id,
startDateTime = mission.startDateTimeUtc.toString(),
endDateTime = mission.endDateTimeUtc?.toString(),
id = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(mission.startDateTimeUtc),
startDateTime = mission.startDateTimeUtc,
endDateTime = mission.endDateTimeUtc,
presenceMer = durations["atSeaDurations"].orEmpty(),
presenceQuai = durations["dockingDurations"].orEmpty(),
indisponibilite = durations["unavailabilityDurations"].orEmpty(),
nbJoursMer = 0,
nbJoursMer = nbOfDaysAtSea,
dureeMission = missionDuration,
patrouilleEnv = 0,
patrouilleMigrant = 0,
distanceMilles = generalInfo?.distanceInNauticalMiles,
goMarine = generalInfo?.consumedGOInLiters,
essence = generalInfo?.consumedFuelInLiters,
crew = agentsCrew,
timeline = timeline
timeline = formatActionsForTimeline.formatForRapportNav1(timeline)
)

return exportRepository.exportOdt(exportParams)
} catch (e: Exception) {
logger.error("[exportOdt] error occurred during exportOdt: ${e.message}")
logger.error("[RapportDePatrouille] - Error building data before sending it to RapportNav1: ${e.message}")
return null
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,18 @@ 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.status.mapActionStatusTypeToHumanString
import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.action.GroupActionByDate
import fr.gouv.dgampa.rapportnav.infrastructure.rapportnav1.adapters.inputs.TimelineActionItem
import fr.gouv.dgampa.rapportnav.infrastructure.rapportnav1.adapters.inputs.TimelineActions
import java.time.LocalDate
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter

@UseCase
class FormatActionsForTimeline(
private val groupActionByDate: GroupActionByDate,
) {

fun formatTimeline(actions: List<MissionActionEntity>?): Map<String, List<String>>? {
fun formatTimeline(actions: List<MissionActionEntity>?): Map<LocalDate, List<String>>? {

if (actions.isNullOrEmpty()) {
return null
Expand All @@ -34,6 +38,15 @@ class FormatActionsForTimeline(
}
}

fun formatForRapportNav1(actions: Map<LocalDate, List<String>>?): List<TimelineActions> {
return actions?.map { (date, actionsAsString) ->
TimelineActions(
date = date.toString(),
freeNote = actionsAsString.map { TimelineActionItem(it) }
)
} ?: emptyList()
}

private fun formatAction(action: MissionActionEntity): String? {
return when (action) {
is MissionActionEntity.EnvAction -> formatEnvAction(action)
Expand Down Expand Up @@ -65,8 +78,8 @@ class FormatActionsForTimeline(
}


fun formatTime(dateTime: ZonedDateTime?): String? {
return dateTime?.toLocalTime()?.toString()?.padStart(5, '0') ?: "N/A"
fun formatTime(dateTime: ZonedDateTime?): String {
return dateTime?.format(DateTimeFormatter.ofPattern("HH:mm")) ?: "N/A"
}


Expand All @@ -76,7 +89,7 @@ class FormatActionsForTimeline(
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" } ?: ""
val amountOfControls = action.actionNumberOfControls?.let { " - $it contrôle(s)" } ?: ""
return "$startTime / $endTime - Contrôle Environnement$facade$themes$amountOfControls"
}
}
Expand All @@ -88,15 +101,15 @@ class FormatActionsForTimeline(
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() }
action.gearInfractions.map { it.natinf },
action.logbookInfractions.map { it.natinf },
action.speciesInfractions.map { it.natinf },
action.otherInfractions.map { it.natinf }
).flatten().distinct().let { list ->
if (list.isEmpty()) {
" - RAS"
} else {
" - NATINF: ${list.joinToString(" + ")}"
" - NATINF: ${list.filterNotNull().joinToString(" + ")}"
}
}
val pvCount = listOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,27 @@ import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.ActionStatus
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 kotlin.time.DurationUnit

@UseCase
class MapStatusDurations(
private val getStatusDurations: GetStatusDurations,
) {

private inline fun List<GetStatusDurations.ActionStatusWithDuration>.findDuration(predicate: (GetStatusDurations.ActionStatusWithDuration) -> Boolean): Int {
return find(predicate)?.value?.toInt() ?: 0
return find(predicate)?.duration?.toInt() ?: 0
}

fun execute(mission: MissionEntity, statuses: List<ActionStatusEntity>): Map<String, Map<String, Int>> {
val durations = getStatusDurations.computeActionDurations(
fun execute(
mission: MissionEntity,
statuses: List<ActionStatusEntity>,
durationUnit: DurationUnit = DurationUnit.SECONDS
): Map<String, Map<String, Int>> {
val durations = getStatusDurations.computeActionDurationsForAllMission(
missionStartDateTime = mission.startDateTimeUtc,
missionEndDateTime = mission.endDateTimeUtc,
actions = statuses,
durationUnit = durationUnit
)

val atSeaDurations = mapOf(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
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.ActionStatusType
import java.time.ZonedDateTime
import kotlin.time.DurationUnit

@UseCase
class GetNbOfDaysAtSeaFromNavigationStatus(
private val getStatusDurations: GetStatusDurations,
) {

/**
* Computes the amount of days with min 4h of navigation
* En français: un jour de mer est décompté dès que le navire effectue plus de 4 heures de navigation en mer.
*
* This function will get the duration for every Navigating status, group them by date,
* check whether it exceeds 4 hours and sum it all up
*
* @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.
* @param durationUnit which duration unit (seconds, minutes, hours)
* @return An integer representing the amount of days with min 4h of navigation
*/
fun execute(
missionStartDateTime: ZonedDateTime,
missionEndDateTime: ZonedDateTime? = null,
actions: List<ActionStatusEntity>? = listOf(),
durationUnit: DurationUnit = DurationUnit.HOURS
): Int {
if (actions.isNullOrEmpty()) {
return 0
}
val statusesWithDurations = getStatusDurations.computeActionDurationsPerAction(
missionStartDateTime = missionStartDateTime,
missionEndDateTime = missionEndDateTime,
actions = actions,
durationUnit = durationUnit
).filter { it.status === ActionStatusType.NAVIGATING }
val actionsPerDay = statusesWithDurations.groupBy { it.date?.toLocalDate() }
val exceedsFourHoursPerDay = actionsPerDay.mapValues { (_, statuses) ->
statuses.sumOf { it.duration } > 4
}
val numberOfDaysExceedingFourHours = exceedsFourHoursPerDay.count { it.value }
return numberOfDaysExceedingFourHours
}

}
Loading

0 comments on commit b5dd3ad

Please sign in to comment.