Skip to content

Commit

Permalink
[Mission] can detach the only reporting attached to mission
Browse files Browse the repository at this point in the history
  • Loading branch information
claire2212 committed Oct 17, 2023
1 parent 9db2762 commit d37fa85
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,81 +13,86 @@ import fr.gouv.cacem.monitorenv.domain.use_cases.missions.dtos.MissionDTO

@UseCase
class CreateOrUpdateMission(
private val baseRepository: IBaseRepository,
private val departmentRepository: IDepartmentAreaRepository,
private val missionRepository: IMissionRepository,
private val facadeRepository: IFacadeAreasRepository,
private val reportingRepository: IReportingRepository,

private val baseRepository: IBaseRepository,
private val departmentRepository: IDepartmentAreaRepository,
private val missionRepository: IMissionRepository,
private val facadeRepository: IFacadeAreasRepository,
private val reportingRepository: IReportingRepository,
) {
@Throws(IllegalArgumentException::class)
fun execute(mission: MissionEntity?, attachedReportingIds: List<Int>? = null): MissionDTO {
require(mission != null) {
"No mission to create or update"
}
val envActions = mission.envActions?.map {
when (it.actionType) {
ActionTypeEnum.CONTROL -> {
(it as EnvActionControlEntity).copy(
facade = (
it.geom
?: mission.geom
)?.let { geom -> facadeRepository.findFacadeFromGeometry(geom) },
department = (
it.geom
?: mission.geom
)?.let { geom -> departmentRepository.findDepartmentFromGeometry(geom) },
)
}

ActionTypeEnum.SURVEILLANCE -> {
val surveillance = it as EnvActionSurveillanceEntity
/*
When coverMissionZone is true, use mission geometry in priority, fall back to action geometry.
When coverMissionZone is not true, prioritize the other way around.
Ideally the fallbacks should not be needed, but if coverMissionZone is true and the mission geom
is null, or if coverMissionZone is false and the action geom is null, then rather that nothing,
better use the geometry that is available, if any.
*/
val geometry = if (surveillance.coverMissionZone == true) {
(
mission.geom
?: surveillance.geom
fun execute(mission: MissionEntity?, attachedReportingIds: List<Int>? = listOf()): MissionDTO {
require(mission != null) { "No mission to create or update" }
val envActions =
mission.envActions?.map {
when (it.actionType) {
ActionTypeEnum.CONTROL -> {
(it as EnvActionControlEntity).copy(
facade =
(it.geom ?: mission.geom)?.let { geom ->
facadeRepository.findFacadeFromGeometry(geom)
},
department =
(it.geom ?: mission.geom)?.let { geom ->
departmentRepository.findDepartmentFromGeometry(
geom,
)
},
)
}
ActionTypeEnum.SURVEILLANCE -> {
val surveillance = it as EnvActionSurveillanceEntity
/*
When coverMissionZone is true, use mission geometry in priority, fall back to action geometry.
When coverMissionZone is not true, prioritize the other way around.
Ideally the fallbacks should not be needed, but if coverMissionZone is true and the mission geom
is null, or if coverMissionZone is false and the action geom is null, then rather that nothing,
better use the geometry that is available, if any.
*/
val geometry =
if (surveillance.coverMissionZone == true) {
(mission.geom ?: surveillance.geom)
} else {
(surveillance.geom ?: mission.geom)
}
surveillance.copy(
facade =
geometry?.let { geom ->
facadeRepository.findFacadeFromGeometry(geom)
},
department =
geometry?.let { geom ->
departmentRepository.findDepartmentFromGeometry(
geom,
)
},
)
} else {
(surveillance.geom ?: mission.geom)
}
ActionTypeEnum.NOTE -> {
(it as EnvActionNoteEntity).copy()
}
}
surveillance.copy(
facade = geometry?.let { geom -> facadeRepository.findFacadeFromGeometry(geom) },
department = geometry?.let { geom -> departmentRepository.findDepartmentFromGeometry(geom) },
)
}

ActionTypeEnum.NOTE -> {
(it as EnvActionNoteEntity).copy()
}
}
}

var facade: String? = null

if (mission.geom != null) {
facade = facadeRepository.findFacadeFromGeometry(mission.geom)
}

val missionToSave = mission.copy(
facade = facade,
envActions = envActions,
)
val missionToSave =
mission.copy(
facade = facade,
envActions = envActions,
)
val savedMission = missionRepository.save(missionToSave)

if (savedMission.mission.id == null) {
throw IllegalArgumentException("Mission id is null")
}

if (attachedReportingIds != null) {
reportingRepository.attachReportingsToMission(attachedReportingIds, savedMission.mission.id)
}
reportingRepository.attachReportingsToMission(
attachedReportingIds ?: listOf(),
savedMission.mission.id,
)

return missionRepository.findById(savedMission.mission.id)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import fr.gouv.cacem.monitorenv.domain.repositories.*
import fr.gouv.cacem.monitorenv.domain.use_cases.reportings.dtos.ReportingDTO
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.time.ZonedDateTime

@UseCase
class CreateOrUpdateReporting(
Expand All @@ -29,23 +28,10 @@ class CreateOrUpdateReporting(
seaFront = facadeRepository.findFacadeFromGeometry(reporting.geom)
}

var attachedToMissionAtUtc: ZonedDateTime? = null
var detachedFromMissionAtUtc: ZonedDateTime? = null
if (reporting.missionId != null && reporting.attachedToMissionAtUtc == null) {
attachedToMissionAtUtc = ZonedDateTime.now()
detachedFromMissionAtUtc = null
}

if (reporting.missionId == null && reporting.attachedToMissionAtUtc != null) {
detachedFromMissionAtUtc = ZonedDateTime.now()
}

val savedReport =
reportingRepository.save(
reporting.copy(
seaFront = seaFront,
attachedToMissionAtUtc = attachedToMissionAtUtc,
detachedFromMissionAtUtc = detachedFromMissionAtUtc,
),
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import fr.gouv.cacem.monitorenv.domain.use_cases.reportings.dtos.ReportingDTO
import fr.gouv.cacem.monitorenv.infrastructure.api.adapters.publicapi.outputs.ControlUnitDataOutput
import org.locationtech.jts.geom.Geometry
import java.time.ZonedDateTime
import java.util.UUID

data class MissionAttachedReportingDataOutput(
val id: Int,
Expand Down Expand Up @@ -36,6 +37,9 @@ data class MissionAttachedReportingDataOutput(
val validityTime: Int? = null,
val isArchived: Boolean,
val openBy: String? = null,
val attachedToMissionAtUtc: ZonedDateTime? = null,
val detachedFromMissionAtUtc: ZonedDateTime? = null,
val attachedEnvActionId: UUID? = null,
) {
companion object {
fun fromReportingDTO(
Expand All @@ -47,7 +51,8 @@ data class MissionAttachedReportingDataOutput(
reportingId = dto.reporting.reportingId,
sourceType = dto.reporting.sourceType,
semaphoreId = dto.reporting.semaphoreId,
semaphore = if (dto.semaphore != null) {
semaphore =
if (dto.semaphore != null) {
SemaphoreDataOutput.fromSemaphoreEntity(
dto.semaphore,
)
Expand All @@ -57,17 +62,19 @@ data class MissionAttachedReportingDataOutput(
controlUnitId = dto.reporting.controlUnitId,
controlUnit =
if (dto.controlUnit != null) {
ControlUnitDataOutput
.fromFullControlUnit(
dto.controlUnit,
)
ControlUnitDataOutput.fromFullControlUnit(
dto.controlUnit,
)
} else {
null
},
displayedSource =
when (dto.reporting.sourceType) {
SourceTypeEnum.SEMAPHORE -> dto?.semaphore?.unit ?: dto?.semaphore?.name
// TODO This is really strange : `fullControlUnit?.controlUnit` can't be null and I have to add another `?`...
SourceTypeEnum.SEMAPHORE ->
dto?.semaphore?.unit
?: dto?.semaphore?.name
// TODO This is really strange : `fullControlUnit?.controlUnit`
// can't be null and I have to add another `?`...
SourceTypeEnum.CONTROL_UNIT -> dto?.controlUnit?.controlUnit?.name
SourceTypeEnum.OTHER -> dto.reporting.sourceName
else -> ""
Expand All @@ -89,6 +96,8 @@ data class MissionAttachedReportingDataOutput(
validityTime = dto.reporting.validityTime,
isArchived = dto.reporting.isArchived,
openBy = dto.reporting.openBy,
attachedToMissionAtUtc = dto.reporting.attachedToMissionAtUtc,
detachedFromMissionAtUtc = dto.reporting.detachedFromMissionAtUtc,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,25 @@ data class MissionModel(
fun toMissionDTO(objectMapper: ObjectMapper): MissionDTO {
return MissionDTO(
mission = this.toMissionEntity(objectMapper),
attachedReportingIds = this.attachedReportings?.map { it.id as Int } ?: listOf(),
attachedReportingIds =
this.attachedReportings
?.filter { it.detachedFromMissionAtUtc == null }
?.map { it.id as Int }
?: listOf(),
attachedReportings =
this.attachedReportings?.map { it.toReportingDTO(objectMapper) }
this.attachedReportings
?.filter { it.detachedFromMissionAtUtc == null }
?.map { it.toReportingDTO(objectMapper) }
?: listOf(),
detachedReportings =
this.attachedReportings
?.filter { it.detachedFromMissionAtUtc != null }
?.map { it.toReportingDTO(objectMapper) }
?: listOf(),
detachedReportingIds =
this.attachedReportings
?.filter { it.detachedFromMissionAtUtc != null }
?.map { it.id as Int }
?: listOf(),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import fr.gouv.cacem.monitorenv.infrastructure.database.repositories.interfaces.
import org.springframework.dao.DataIntegrityViolationException
import org.springframework.dao.InvalidDataAccessApiUsageException
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.Modifying
import org.springframework.stereotype.Repository
import org.springframework.transaction.annotation.Transactional
import java.time.Instant
Expand All @@ -27,6 +28,7 @@ class JpaMissionRepository(
}

@Transactional
@Modifying(clearAutomatically = true, flushAutomatically = true)
override fun delete(missionId: Int) {
dbMissionRepository.delete(missionId)
}
Expand All @@ -49,22 +51,26 @@ class JpaMissionRepository(
missionSources = convertToPGArray(missionSourcesAsStringArray),
seaFronts = convertToPGArray(seaFronts),
pageable = pageable,
).map { it.toMissionDTO(mapper) }
)
.map { it.toMissionDTO(mapper) }
}

override fun findById(missionId: Int): MissionDTO {
return dbMissionRepository.findById(missionId).get().toMissionDTO(mapper)
}

@Transactional
@Modifying(clearAutomatically = true, flushAutomatically = true)
override fun save(mission: MissionEntity): MissionDTO {
return try {
// Extract all control units resources unique baseIds
val uniqueBaseIds = mission.controlUnits.flatMap { controlUnit ->
controlUnit.resources.map { it.baseId }
}.distinct()
val uniqueBaseIds =
mission.controlUnits
.flatMap { controlUnit -> controlUnit.resources.map { it.baseId } }
.distinct()
// Fetch all of them as models
val baseModels = dbBaseRepository.findAllById(uniqueBaseIds).map { BaseModel.fromFullBase(it) }
val baseModels =
dbBaseRepository.findAllById(uniqueBaseIds).map { BaseModel.fromFullBase(it) }
// Create a `[baseId] → BaseModel` map
val baseModelMap = baseModels.associateBy { requireNotNull(it.id) }

Expand All @@ -73,13 +79,14 @@ class JpaMissionRepository(
} catch (e: Exception) {
when (e) {
// TODO Is `InvalidDataAccessApiUsageException` necessary?
is DataIntegrityViolationException, is InvalidDataAccessApiUsageException -> {
is DataIntegrityViolationException,
is InvalidDataAccessApiUsageException,
-> {
throw ControlResourceOrUnitNotFoundException(
"Invalid control unit or resource id: not found in referential.",
e,
)
}

else -> throw e
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ class JpaReportingRepository(
}

@Transactional
@Modifying(clearAutomatically = true, flushAutomatically = true)
override fun delete(reportingId: Int) {
dbReportingRepository.delete(reportingId)
}
Expand All @@ -122,16 +123,19 @@ class JpaReportingRepository(
}

@Transactional
@Modifying(clearAutomatically = true, flushAutomatically = true)
override fun archiveOutdatedReportings(): Int {
return dbReportingRepository.archiveOutdatedReportings()
}

@Transactional
@Modifying(clearAutomatically = true, flushAutomatically = true)
override fun archiveReportings(ids: List<Int>) {
dbReportingRepository.archiveReportings(ids)
}

@Transactional
@Modifying(clearAutomatically = true, flushAutomatically = true)
override fun deleteReportings(ids: List<Int>) {
dbReportingRepository.deleteReportings(ids)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ interface IDBReportingRepository : JpaRepository<ReportingModel, Int> {
SET
mission_id = :missionId,
attached_to_mission_at_utc = CASE WHEN (mission_id IS NULL OR mission_id = (:missionId)) AND id IN (:reportingIds) THEN NOW() ELSE attached_to_mission_at_utc END,
detached_from_mission_at_utc = CASE WHEN id NOT IN (:reportingIds) THEN NOW() ELSE NULL END
WHERE id in (:reportingIds) OR (mission_id = :missionId AND detached_from_mission_at_utc IS NULL)
detached_from_mission_at_utc = CASE WHEN (id NOT IN (:reportingIds) OR (:reportingIds) IS NULL ) THEN NOW() ELSE NULL END
WHERE id IN (:reportingIds) OR (mission_id = :missionId AND detached_from_mission_at_utc IS NULL)
""",
nativeQuery = true,
)
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/domain/use_cases/missions/saveMission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const saveMission =
const {
sideWindow: { currentPath }
} = getState()
const valuesToSave = omit(values, ['attachedReportings'])
const valuesToSave = omit(values, ['attachedReportings', 'detachedReportings', 'detachedReportingIds'])
const routeParams = getMissionPageRoute(currentPath)
const missionIsNewMission = isNewMission(routeParams?.params?.id)

Expand Down

0 comments on commit d37fa85

Please sign in to comment.