diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts index d7e4581db5..6d22e92305 100644 --- a/backend/build.gradle.kts +++ b/backend/build.gradle.kts @@ -72,7 +72,7 @@ dependencies { runtimeOnly("org.postgresql:postgresql:42.6.0") testImplementation("io.ktor:ktor-client-mock-jvm:2.3.3") testImplementation("org.assertj:assertj-core:3.25.0") - testImplementation("org.testcontainers:postgresql:1.19.6") + testImplementation("org.testcontainers:postgresql:1.19.4") testImplementation("org.testcontainers:testcontainers:1.19.4") testImplementation("org.testcontainers:junit-jupiter:1.19.4") testImplementation("jakarta.servlet:jakarta.servlet-api:6.0.0") diff --git a/frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/__tests__/utils.test.ts b/frontend/src/features/ActivityReport/__tests__/utils.test.ts similarity index 68% rename from frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/__tests__/utils.test.ts rename to frontend/src/features/ActivityReport/__tests__/utils.test.ts index e629dc9684..3297e05acc 100644 --- a/frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/__tests__/utils.test.ts +++ b/frontend/src/features/ActivityReport/__tests__/utils.test.ts @@ -1,8 +1,12 @@ import { describe, expect, it } from '@jest/globals' +import { JDP_CSV_MAP_BASE } from '../components/ExportActivityReportsDialog/csvMap' import { JDP } from '../constants' -import { JDP_CSV_MAP_BASE } from '../csvMap' -import { formatDMDCoordinateForActivityReport, getJDPCsvMap } from '../utils' +import { + formatDMDCoordinateForActivityReport, + getJDPCsvMap, + getSpeciesOnboardWithUntargetedSpeciesGrouped +} from '../utils' describe('utils', () => { it('formatCoordinateForActivityReport Should format a latitude', async () => { @@ -29,9 +33,32 @@ describe('utils', () => { expect(latitude).toEqual('') }) + it('getSpeciesOnboardWithUntargetedSpeciesGrouped Should return untargeted species grouped as OTH', async () => { + // Given + const speciesOnboard = [ + { controlledWeight: 500, declaredWeight: 471.2, nbFish: undefined, speciesCode: 'HKE', underSized: true }, + { controlledWeight: undefined, declaredWeight: 13.46, nbFish: undefined, speciesCode: 'BLI', underSized: false }, + { controlledWeight: 123.6, declaredWeight: undefined, nbFish: undefined, speciesCode: 'COD', underSized: false }, + { controlledWeight: undefined, declaredWeight: 12.6, nbFish: undefined, speciesCode: 'ANZ', underSized: false }, + { controlledWeight: undefined, declaredWeight: 45.5, nbFish: undefined, speciesCode: 'FMI', underSized: false } + ] + + // When + const groupedSpeciesOnboard = getSpeciesOnboardWithUntargetedSpeciesGrouped(speciesOnboard, ['ANZ', 'HKE', 'ATJ']) + + // Then + expect(groupedSpeciesOnboard).toHaveLength(3) + expect(groupedSpeciesOnboard[0]?.speciesCode).toEqual('HKE') + expect(groupedSpeciesOnboard[0]?.controlledWeight).toEqual(500) + expect(groupedSpeciesOnboard[1]?.speciesCode).toEqual('OTH') + expect(groupedSpeciesOnboard[1]?.declaredWeight).toEqual(182.56) + expect(groupedSpeciesOnboard[2]?.speciesCode).toEqual('ANZ') + expect(groupedSpeciesOnboard[2]?.declaredWeight).toEqual(12.6) + }) + it('getJDPCsvMap Should be dynamically generated with species, infractions and control comment for WESTERN_WATERS', async () => { // When - const csvMap = getJDPCsvMap(JDP_CSV_MAP_BASE, JDP.WESTERN_WATERS, ['ANZ', 'HKE']) + const csvMap = getJDPCsvMap(JDP_CSV_MAP_BASE, JDP.WESTERN_WATERS) // Then expect(Object.keys(csvMap)).toHaveLength(91) @@ -67,7 +94,7 @@ describe('utils', () => { it('getJDPCsvMap Should be dynamically generated with species, infractions and control comment for MEDITERRANEAN_AND_EASTERN_ATLANTIC', async () => { // When - const csvMap = getJDPCsvMap(JDP_CSV_MAP_BASE, JDP.MEDITERRANEAN_AND_EASTERN_ATLANTIC, []) + const csvMap = getJDPCsvMap(JDP_CSV_MAP_BASE, JDP.MEDITERRANEAN_AND_EASTERN_ATLANTIC) // Then expect(Object.keys(csvMap)).toHaveLength(91) diff --git a/frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/apis.ts b/frontend/src/features/ActivityReport/apis.ts similarity index 90% rename from frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/apis.ts rename to frontend/src/features/ActivityReport/apis.ts index 386f98d96d..a5fa5ff42b 100644 --- a/frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/apis.ts +++ b/frontend/src/features/ActivityReport/apis.ts @@ -1,4 +1,4 @@ -import { monitorfishApi } from '../../../../api/api' +import { monitorfishApi } from '@api/api' import type { ActivityReport } from './types' diff --git a/frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/csvMap.ts b/frontend/src/features/ActivityReport/components/ExportActivityReportsDialog/csvMap.ts similarity index 89% rename from frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/csvMap.ts rename to frontend/src/features/ActivityReport/components/ExportActivityReportsDialog/csvMap.ts index d6d32aedc0..ccdbbda3c6 100644 --- a/frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/csvMap.ts +++ b/frontend/src/features/ActivityReport/components/ExportActivityReportsDialog/csvMap.ts @@ -1,18 +1,18 @@ import { customDayjs } from '@mtes-mct/monitor-ui' import { toAlpha3 } from 'i18n-iso-countries' -import { formatDMDCoordinateForActivityReport, getPatrolType } from './utils' import { getCoordinates } from '../../../../coordinates' import { CoordinatesFormat, WSG84_PROJECTION } from '../../../../domain/entities/map/constants' +import { formatDMDCoordinateForActivityReport, getPatrolType } from '../../utils' -import type { ActivityReportWithId } from './types' -import type { DownloadAsCsvMap } from '../../../../utils/downloadAsCsv' +import type { ActivityReportWithId } from '../../types' +import type { DownloadAsCsvMap } from '@utils/downloadAsCsv' /* eslint-disable sort-keys-fix/sort-keys-fix */ export const JDP_CSV_MAP_BASE: DownloadAsCsvMap = { patrolCode: { label: 'PATROL_CODE', - transform: activity => getPatrolType(activity) + (activity.controlUnits[0]?.name || '') + transform: activity => getPatrolType(activity) + (activity.controlUnits[0]?.name ?? '') }, patrolType: { label: 'PATROL_TYPE', @@ -20,7 +20,7 @@ export const JDP_CSV_MAP_BASE: DownloadAsCsvMap = { }, controlUnit: { label: 'MEAN_ID', - transform: activity => activity.controlUnits[0]?.name || '' + transform: activity => activity.controlUnits[0]?.name ?? '' }, jdpCode: 'JDP_CODE', eventType: { @@ -85,15 +85,15 @@ export const JDP_CSV_MAP_BASE: DownloadAsCsvMap = { meshSize: { label: 'MESH_SIZE', transform: activity => - activity.action.gearOnboard[0]?.controlledMesh || activity.action.gearOnboard[0]?.declaredMesh || '' + activity.action.gearOnboard[0]?.controlledMesh ?? (activity.action.gearOnboard[0]?.declaredMesh || '') }, faoArea: { label: 'FAO_AREA_CODE', - transform: activity => activity.action.faoAreas[0] || '' + transform: activity => activity.action.faoAreas[0] ?? '' }, fleetSegment: { label: 'FLEET_SEGMENT', - transform: activity => activity.action.segments[0]?.segment || '' + transform: activity => activity.action.segments[0]?.segment ?? '' }, latitude: { label: 'LA', diff --git a/frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/index.tsx b/frontend/src/features/ActivityReport/components/ExportActivityReportsDialog/index.tsx similarity index 95% rename from frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/index.tsx rename to frontend/src/features/ActivityReport/components/ExportActivityReportsDialog/index.tsx index 8828f4810d..eee5fc68a7 100644 --- a/frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/index.tsx +++ b/frontend/src/features/ActivityReport/components/ExportActivityReportsDialog/index.tsx @@ -1,3 +1,4 @@ +import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { Accent, Button, @@ -11,9 +12,8 @@ import { import { useState } from 'react' import styled from 'styled-components' -import { JDP } from './constants' -import { downloadActivityReports, NO_ACTIVITY_REPORT } from './useCases/downloadActivityReports' -import { useMainAppDispatch } from '../../../../hooks/useMainAppDispatch' +import { JDP } from '../../constants' +import { downloadActivityReports, NO_ACTIVITY_REPORT } from '../../useCases/downloadActivityReports' import type { Promisable } from 'type-fest' diff --git a/frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/constants.ts b/frontend/src/features/ActivityReport/constants.ts similarity index 77% rename from frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/constants.ts rename to frontend/src/features/ActivityReport/constants.ts index ead461936a..50c4eddad5 100644 --- a/frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/constants.ts +++ b/frontend/src/features/ActivityReport/constants.ts @@ -4,4 +4,4 @@ export enum JDP { WESTERN_WATERS = 'JDP WW-01' } -export const NOT_TARGETED_SPECIES_CODE = 'OTH' +export const UNTARGETED_SPECIES_CODE = 'OTH' diff --git a/frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/types.ts b/frontend/src/features/ActivityReport/types.ts similarity index 63% rename from frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/types.ts rename to frontend/src/features/ActivityReport/types.ts index 7b2f503f71..1a1ed8a8bb 100644 --- a/frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/types.ts +++ b/frontend/src/features/ActivityReport/types.ts @@ -1,6 +1,6 @@ -import type { Vessel } from '../../../../domain/entities/vessel/types' -import type { LegacyControlUnit } from '../../../../domain/types/legacyControlUnit' -import type { MissionAction } from '../../../../domain/types/missionAction' +import type { Vessel } from '../../domain/entities/vessel/types' +import type { LegacyControlUnit } from '../../domain/types/legacyControlUnit' +import type { MissionAction } from '../../domain/types/missionAction' export type ActivityReports = { activityReports: ActivityReport[] diff --git a/frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/useCases/downloadActivityReports.ts b/frontend/src/features/ActivityReport/useCases/downloadActivityReports.ts similarity index 77% rename from frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/useCases/downloadActivityReports.ts rename to frontend/src/features/ActivityReport/useCases/downloadActivityReports.ts index 64e1036663..c1cebd3bcb 100644 --- a/frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/useCases/downloadActivityReports.ts +++ b/frontend/src/features/ActivityReport/useCases/downloadActivityReports.ts @@ -1,11 +1,10 @@ import { customDayjs, logSoftError } from '@mtes-mct/monitor-ui' -import { sortBy } from 'lodash' +import { downloadAsCsv } from '@utils/downloadAsCsv' -import { downloadAsCsv } from '../../../../../utils/downloadAsCsv' import { activityReportApi } from '../apis' +import { JDP_CSV_MAP_BASE } from '../components/ExportActivityReportsDialog/csvMap' import { JDP } from '../constants' -import { JDP_CSV_MAP_BASE } from '../csvMap' -import { getJDPCsvMap } from '../utils' +import { getJDPCsvMap, getSpeciesOnboardWithUntargetedSpeciesGrouped } from '../utils' import type { ActivityReports } from '../types' @@ -30,14 +29,13 @@ export const downloadActivityReports = (afterDateTime: string, beforeDateTime: s ...activity, action: { ...activity.action, - // We sort species by weight as only 10 species columns are contained in the CSV - speciesOnboard: sortBy(activity.action.speciesOnboard, ({ declaredWeight }) => declaredWeight).reverse() + speciesOnboard: getSpeciesOnboardWithUntargetedSpeciesGrouped(activity.action.speciesOnboard, jdpSpecies) }, id: index })) const fileName = getCsvFileName(jdp) - const csvMap = getJDPCsvMap(JDP_CSV_MAP_BASE, jdp, jdpSpecies) + const csvMap = getJDPCsvMap(JDP_CSV_MAP_BASE, jdp) downloadAsCsv(fileName, activityReportsWithId, csvMap) } diff --git a/frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/utils.ts b/frontend/src/features/ActivityReport/utils.ts similarity index 69% rename from frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/utils.ts rename to frontend/src/features/ActivityReport/utils.ts index cd5b93e5f1..b2f8c82af6 100644 --- a/frontend/src/features/SideWindow/MissionList/ExportActivityReportsDialog/utils.ts +++ b/frontend/src/features/ActivityReport/utils.ts @@ -1,10 +1,10 @@ -import { range } from 'lodash' +import { range, sortBy } from 'lodash' -import { JDP, NOT_TARGETED_SPECIES_CODE } from './constants' -import { MissionAction } from '../../../../domain/types/missionAction' +import { JDP, UNTARGETED_SPECIES_CODE } from './constants' +import { MissionAction } from '../../domain/types/missionAction' import type { ActivityReportWithId } from './types' -import type { DownloadAsCsvMap } from '../../../../utils/downloadAsCsv' +import type { DownloadAsCsvMap } from '@utils/downloadAsCsv' export function formatDMDCoordinateForActivityReport(coordinate: string | undefined): string { const hemisphere = coordinate?.at(-1) @@ -17,7 +17,10 @@ export function formatDMDCoordinateForActivityReport(coordinate: string | undefi return `${hemisphere}${nextCoordinate}` } -export function getJDPCsvMap(baseCsvMap: DownloadAsCsvMap, jdp: JDP, jdpSpecies: string[]) { +/** + * Adds 'SPECIES' related columns, 'INFR' related columns and 'COMMENT' column to JDP_CSV_MAP_BASE + */ +export function getJDPCsvMap(baseCsvMap: DownloadAsCsvMap, jdp: JDP) { const numberOfSpeciesColumns = 10 const numberOfInfractionColumns = 10 @@ -33,10 +36,6 @@ export function getJDPCsvMap(baseCsvMap: DownloadAsCsvMap, return '' } - if (!jdpSpecies.includes(speciesOnboard.speciesCode)) { - return NOT_TARGETED_SPECIES_CODE - } - return speciesOnboard.speciesCode } } @@ -47,7 +46,7 @@ export function getJDPCsvMap(baseCsvMap: DownloadAsCsvMap, transform: activity => { const speciesOnboard = activity.action.speciesOnboard[count - 1] - return speciesOnboard?.controlledWeight || speciesOnboard?.declaredWeight || '' + return speciesOnboard?.controlledWeight ?? speciesOnboard?.declaredWeight ?? '' } } @@ -57,7 +56,7 @@ export function getJDPCsvMap(baseCsvMap: DownloadAsCsvMap, transform: activity => { const speciesOnboard = activity.action.speciesOnboard[count - 1] - return speciesOnboard?.nbFish || '' + return speciesOnboard?.nbFish ?? '' } } }) @@ -74,7 +73,7 @@ export function getJDPCsvMap(baseCsvMap: DownloadAsCsvMap, transform: activity => { const allNatinfs = getInfractionsKeys(activity.action, 'natinf') - return allNatinfs[count - 1] || '' + return allNatinfs[count - 1] ?? '' } } @@ -84,7 +83,7 @@ export function getJDPCsvMap(baseCsvMap: DownloadAsCsvMap, transform: activity => { const allComments = getInfractionsKeys(activity.action, 'comments') - return allComments[count - 1] || '' + return allComments[count - 1] ?? '' } } }) @@ -95,6 +94,31 @@ export function getJDPCsvMap(baseCsvMap: DownloadAsCsvMap, return baseCsvMap } +export function getSpeciesOnboardWithUntargetedSpeciesGrouped( + speciesOnboard: MissionAction.SpeciesControl[], + jdpSpecies: string[] +): MissionAction.SpeciesControl[] { + const otherSpeciesSummedWeight = speciesOnboard + .filter(species => !jdpSpecies.includes(species.speciesCode)) + .map(species => species.controlledWeight ?? species.declaredWeight) + .filter((species): species is number => species !== undefined) + .reduce((accumulator, currentValue) => accumulator + currentValue, 0) + + const otherSpecy = { + controlledWeight: undefined, + declaredWeight: otherSpeciesSummedWeight, + nbFish: undefined, + speciesCode: UNTARGETED_SPECIES_CODE, + underSized: undefined + } + + const speciesOnboardWithoutOtherSpecies = speciesOnboard.filter(species => jdpSpecies.includes(species.speciesCode)) + + const groupedSpeciesOnboard = speciesOnboardWithoutOtherSpecies.concat(otherSpecy) + + return sortBy(groupedSpeciesOnboard, ({ declaredWeight }) => declaredWeight).reverse() +} + function getInfractionsKeys(action: MissionAction.MissionAction, key: string): string[] { return ([] as string[]).concat.apply( [], diff --git a/frontend/src/features/SideWindow/MissionList/index.tsx b/frontend/src/features/SideWindow/MissionList/index.tsx index f6ce606b7d..7d2fc8630f 100644 --- a/frontend/src/features/SideWindow/MissionList/index.tsx +++ b/frontend/src/features/SideWindow/MissionList/index.tsx @@ -4,7 +4,6 @@ import { useCallback, useState } from 'react' import styled from 'styled-components' import { MISSION_LIST_SUB_MENU_OPTIONS, MISSION_LIST_TABLE_OPTIONS } from './constants' -import { ExportActivityReportsDialog } from './ExportActivityReportsDialog' import { FilterBar } from './FilterBar' import { hasSomeOngoingActions, renderStatus } from './utils' import { SEA_FRONT_GROUP_SEA_FRONTS, SeaFrontGroup } from '../../../domain/entities/seaFront/constants' @@ -14,6 +13,7 @@ import { useMainAppSelector } from '../../../hooks/useMainAppSelector' import { useTable } from '../../../hooks/useTable' import { EmptyCardTable } from '../../../ui/card-table/EmptyCardTable' import { NoRsuiteOverrideWrapper } from '../../../ui/NoRsuiteOverrideWrapper' +import { ExportActivityReportsDialog } from '../../ActivityReport/components/ExportActivityReportsDialog' import { useGetFilteredMissionsQuery } from '../../Mission/components/MissionList/hooks/useGetFilteredMissionsQuery' import { missionListActions } from '../../Mission/components/MissionList/slice' import { addMission } from '../../Mission/useCases/addMission'