Skip to content

Commit

Permalink
Merge pull request #402 from MTES-MCT/back-aem-mission-list-ulam
Browse files Browse the repository at this point in the history
[backend] feat:enable AEM export from a list of Missions
  • Loading branch information
aleckvincent authored Nov 13, 2024
2 parents f5d1ca2 + 0cced2f commit 6082743
Show file tree
Hide file tree
Showing 13 changed files with 461 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package fr.gouv.dgampa.rapportnav.domain.entities.aem

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.action.ActionType
import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.action.NavActionEntity
import org.slf4j.LoggerFactory
import java.time.Instant

Expand Down Expand Up @@ -50,5 +48,19 @@ data class AEMTableExport(
tableExport.sovereignProtect?.nbrOfRecognizedVessel = mission.generalInfo?.nbrOfRecognizedVessel?.toDouble();
return tableExport
}

fun fromMissionList(missions: List<MissionEntity>): List<AEMTableExport> {
val tableExports = mutableListOf<AEMTableExport>()

for (mission in missions) {
if (mission.actions != null) {
val tableExport = fromMissionAction(mission.actions ?: listOf(), mission.endDateTimeUtc)
tableExport.sovereignProtect?.nbrOfRecognizedVessel = mission.generalInfo?.nbrOfRecognizedVessel?.toDouble()
tableExports.add(tableExport)
}
}

return tableExports
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export

import fr.gouv.dgampa.rapportnav.config.UseCase
import fr.gouv.dgampa.rapportnav.domain.entities.aem.AEMTableExport
import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionEntity
import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionAEMExportEntity
import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.GetMission
import fr.gouv.dgampa.rapportnav.domain.use_cases.utils.FillAEMExcelRow
import fr.gouv.dgampa.rapportnav.infrastructure.utils.Base64Converter
import fr.gouv.dgampa.rapportnav.infrastructure.utils.FileUtils
import fr.gouv.dgampa.rapportnav.infrastructure.utils.office.ExportExcelFile
import fr.gouv.dgampa.rapportnav.infrastructure.utils.office.OfficeConverter
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardCopyOption

@UseCase
class ExportZipMissionsAEM(
private val getMissionById: GetMission,
@Value("\${rapportnav.aem.template.path}") private val aemTemplatePath: String,
@Value("\${rapportnav.aem.tmp_xlsx.path}") private val aemTmpXLSXPath: String,
private val fillAEMExcelRow: FillAEMExcelRow
) {

private val logger: Logger = LoggerFactory.getLogger(ExportZipMissionsAEM::class.java)

fun execute(missionIds: List<Int>): MissionAEMExportEntity {
var missions = mutableListOf<MissionEntity>()

for (missionId in missionIds) {
val mission = getMissionById.execute(missionId)
if (mission != null) {
missions.add(mission)
}
}

if (missions.size > 0) {

val inputStream = javaClass.getResourceAsStream(aemTemplatePath)
?: throw IllegalArgumentException("Template file not found: $aemTemplatePath")

val tmpPath = Path.of(aemTmpXLSXPath)
Files.copy(inputStream, tmpPath, StandardCopyOption.REPLACE_EXISTING)
inputStream.close()

val excelFile = ExportExcelFile(tmpPath.toString())

val filesToZip = mutableListOf<File>();

for (mission in missions) {
val tableExport = AEMTableExport.fromMission(mission)
fillAEMExcelRow.fill(tableExport, excelFile, "Synthese", 3)
excelFile.save()

logger.info("Excel file processed and saved")

val odsFilePath = OfficeConverter().convert(tmpPath.toString(), "Mission-${mission.id}.ods")
filesToZip.add(File(odsFilePath))
}

val zipFile = File("tmp_output.zip")

val fileUtils = FileUtils();

val outputZipFile = fileUtils.zip(zipFile, filesToZip)

val base64Content = Base64Converter().convertToBase64(outputZipFile.absolutePath)

for (file in filesToZip) {
file.delete() // remove file from project
}

outputZipFile.delete() // remove zip from project

return MissionAEMExportEntity(
fileName = "tableaux_aem.zip",
fileContent = base64Content
)

}
throw RuntimeException("Error : Mission list is empty")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.v2

import fr.gouv.dgampa.rapportnav.config.UseCase
import fr.gouv.dgampa.rapportnav.domain.entities.aem.AEMTableExport
import fr.gouv.dgampa.rapportnav.domain.entities.mission.MissionEntity
import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionAEMExportEntity
import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.GetMission
import fr.gouv.dgampa.rapportnav.domain.use_cases.utils.FillAEMExcelRow
import fr.gouv.dgampa.rapportnav.infrastructure.utils.Base64Converter
import fr.gouv.dgampa.rapportnav.infrastructure.utils.office.ExportExcelFile
import fr.gouv.dgampa.rapportnav.infrastructure.utils.office.OfficeConverter
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardCopyOption

@UseCase
class ExportMissionsAEM(
@Value("\${rapportnav.aem.template.path}") private val aemTemplatePath: String,
@Value("\${rapportnav.aem.tmp_xlsx.path}") private val aemTmpXLSXPath: String,
@Value("\${rapportnav.aem.tmp_ods.path}") private val aemTmpODSPath: String,
private val fillAEMExcelRow: FillAEMExcelRow,
private val getMissionById: GetMission,

) {
private val logger: Logger = LoggerFactory.getLogger(ExportMissionsAEM::class.java)

fun execute(missionIds: List<Int>): MissionAEMExportEntity?
{

var missions = mutableListOf<MissionEntity>()

for (missionId in missionIds) {
val mission = getMissionById.execute(missionId)
if (mission != null) {
missions.add(mission)
}
}

return try {
val inputStream = javaClass.getResourceAsStream(aemTemplatePath)
?: throw IllegalArgumentException("Template file not found: $aemTemplatePath")

val tmpPath = Path.of(aemTmpXLSXPath)
Files.copy(inputStream, tmpPath, StandardCopyOption.REPLACE_EXISTING)
inputStream.close()

logger.info("Template file copied to temporary path: $tmpPath")

val excelFile = ExportExcelFile(tmpPath.toString())
val tableExportList = AEMTableExport.fromMissionList(missions)

if (tableExportList.isNotEmpty()) {
var rowStart = 3

for (tableExport in tableExportList) {
fillAEMExcelRow.fill(tableExport, excelFile, "Synthese", rowStart)
rowStart++
}
excelFile.save()

logger.info("Excel file processed and saved for export on missions list")

val odsFile = OfficeConverter().convert(tmpPath.toString(), aemTmpODSPath)
val base64Content = Base64Converter().convertToBase64(odsFile)

return MissionAEMExportEntity(
fileName = "Rapport_AEM.ods",
fileContent = base64Content
)
}

logger.error("Actions in missions list are null")
null
} catch (e: Exception) {
logger.error("An error occurred during mission processing", e)
null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionExpor
import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.*
import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.ExportMissionRapportPatrouille
import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.ExportMissionAEM
import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.ExportZipMissionsAEM
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
import fr.gouv.dgampa.rapportnav.domain.use_cases.user.GetUserFromToken
import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.adapters.MissionEnvInput
import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.adapters.export.MissionAEMExportInput
import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.adapters.generalInfo.MissionGeneralInfoInput
import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.adapters.generalInfo.MissionServiceInput
import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.model.Mission
Expand All @@ -38,7 +40,8 @@ class MissionController(
private val exportMissionRapportPatrouille: ExportMissionRapportPatrouille,
private val updateMissionService: UpdateMissionService,
private val patchEnvMission: PatchEnvMission,
private val exportExcelFile: ExportMissionAEM
private val exportMissionAEM: ExportMissionAEM,
private val exportZipMissionsAEM: ExportZipMissionsAEM
) {

private val logger = LoggerFactory.getLogger(MissionController::class.java)
Expand Down Expand Up @@ -216,6 +219,10 @@ class MissionController(

@QueryMapping
fun missionAEMExport(@Argument missionId: Int): MissionAEMExportEntity? {
return exportExcelFile.execute(missionId)
return exportMissionAEM.execute(missionId)
}
@QueryMapping
fun missionAEMExportZip(@Argument exportZip: MissionAEMExportInput): MissionAEMExportEntity? {
return exportZipMissionsAEM.execute(exportZip.ids)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package fr.gouv.dgampa.rapportnav.infrastructure.api.bff.adapters.export

data class MissionAEMExportInput(
var ids: List<Int>
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package fr.gouv.dgampa.rapportnav.infrastructure.api.bff.v2

import fr.gouv.dgampa.rapportnav.domain.entities.mission.nav.export.MissionAEMExportEntity
import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.v2.ExportMissionsAEM
import fr.gouv.dgampa.rapportnav.infrastructure.api.bff.adapters.export.MissionAEMExportInput
import org.springframework.graphql.data.method.annotation.Argument
import org.springframework.graphql.data.method.annotation.QueryMapping
import org.springframework.stereotype.Controller

@Controller
class MissionExportController(
private val exportMissionsAEM: ExportMissionsAEM
)
{
@QueryMapping
fun missionAEMExportV2(@Argument missionExportAEMExportInput: MissionAEMExportInput): MissionAEMExportEntity? {
return exportMissionsAEM.execute(missionExportAEMExportInput.ids)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package fr.gouv.dgampa.rapportnav.infrastructure.utils

import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream

class FileUtils(
) {

fun zip(outputZipFile: File, files: List<File>): File {
ZipOutputStream(FileOutputStream(outputZipFile)).use { zipOut ->
files.forEach { file ->
FileInputStream(file).use { fis ->
val zipEntry = ZipEntry(file.name)
zipOut.putNextEntry(zipEntry)
fis.copyTo(zipOut)
zipOut.closeEntry()
}
}
}
return outputZipFile
}
}
6 changes: 6 additions & 0 deletions backend/src/main/resources/graphql/mission.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ type Query {
mission(missionId: ID): Mission
missionGeneralInfo(missionId: ID): MissionGeneralInfo
missionAEMExport(missionId: ID): MissionExport
missionAEMExportZip(exportZip: MissionAEMExportInput): MissionExport
missionAEMExportV2(missionIds: MissionAEMExportInput): MissionExport
}

type Mutation {
Expand Down Expand Up @@ -86,3 +88,7 @@ input MissionEnvInput {
startDateTimeUtc: Instant
endDateTimeUtc: Instant
}

input MissionAEMExportInput {
ids: [Int]
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
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.nav.export.MissionAEMExportEntity
import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.GetMission
import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.ExportMissionAEM
import fr.gouv.dgampa.rapportnav.domain.use_cases.utils.FillAEMExcelRow
import fr.gouv.gmampa.rapportnav.mocks.mission.MissionEntityMock
import fr.gouv.gmampa.rapportnav.mocks.mission.action.NavActionControlMock
import org.assertj.core.api.Assertions
import org.junit.jupiter.api.Test
import org.mockito.Mockito
Expand Down Expand Up @@ -33,14 +37,12 @@ class ExportMissionAEMTests {
Assertions.assertThat(result).isNull()

}
/*

@Test
fun `execute AEM export return a MissionAEMExportEntity when mission and action exist`() {
val action = NavActionControlMock.createActionControlEntity().toNavActionEntity()
val action = NavActionControlMock.create().toNavActionEntity()
val missionAction = MissionActionEntity.NavAction(action)

val missionId = 1
val mission = MissionEntityMock.create(actions = listOf(missionAction))
Mockito.`when`(getMissionById.execute(missionId)).thenReturn(mission)
Expand All @@ -50,5 +52,5 @@ class ExportMissionAEMTests {
Assertions.assertThat(result).isNotNull()
Assertions.assertThat(result).isInstanceOf(MissionAEMExportEntity::class.java)

}*/
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
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.nav.export.MissionAEMExportEntity
import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.GetMission
import fr.gouv.dgampa.rapportnav.domain.use_cases.mission.export.ExportZipMissionsAEM
import fr.gouv.dgampa.rapportnav.domain.use_cases.utils.FillAEMExcelRow
import fr.gouv.gmampa.rapportnav.mocks.mission.MissionEntityMock
import fr.gouv.gmampa.rapportnav.mocks.mission.action.NavActionControlMock
import org.assertj.core.api.Assertions
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

@SpringBootTest(classes = [ExportZipMissionsAEM::class])
class ExportZipMissionsAEMTests {

@MockBean
private lateinit var getMissionById: GetMission

@MockBean
private lateinit var fillAEMExcelRow: FillAEMExcelRow

@Autowired
private lateinit var exportZipMissionsAEM: ExportZipMissionsAEM


@Test
fun `should return a MissionExportAEMEntity`() {
val action = NavActionControlMock.create().toNavActionEntity()
val missionAction = MissionActionEntity.NavAction(action)
val mission = MissionEntityMock.create(actions = listOf(missionAction))
val mission2 = MissionEntityMock.create(actions = listOf(missionAction), id = 2)

Mockito.`when`(getMissionById.execute(1)).thenReturn(mission)
Mockito.`when`(getMissionById.execute(2)).thenReturn(mission2)

val missionAEMExport = exportZipMissionsAEM.execute(listOf(1, 2))

Assertions.assertThat(missionAEMExport).isInstanceOf(MissionAEMExportEntity::class.java)
}
}
Loading

0 comments on commit 6082743

Please sign in to comment.