Skip to content

Commit

Permalink
Merge pull request #106 from MTES-MCT/rapportage-v1.2
Browse files Browse the repository at this point in the history
Rapportage v1.2
  • Loading branch information
thoomasbro authored Aug 28, 2022
2 parents 3f331a7 + 426a40a commit 71b92cc
Show file tree
Hide file tree
Showing 37 changed files with 327 additions and 338 deletions.
42 changes: 29 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
# monitorenv

MonitorEnv est une application développée pour le CACEM afin de centraliser les outils nécessaires à l'exercice de ses missions - la protection de l'environnement marin et le contrôle des activités les plus à risque pour la biodiversité marine -, à savoir:

- l'appui, l'orientation et la coordination des contrôles sur le terrain
- le suivi et le rapportage des missions de contrôle
- l'analyse des données de contrôle, l'aide à la décision et à la définiton des plans de contrôle

MonitorEnv est constituté de deux briques logicielles :

- une application web (MonitorEnv)
- un pipeline de traitement de données (datapipeline).


Le code source de monitorenv est fortement inspiré de [MonitorFish](https://github.com/MTES-MCT/monitorfish). Il en reprend l'architecture ainsi que de nombreux composants. Le code initial doit fortement à ce projet ainsi qu'à leurs auteurs: Loup Théron, Vincent Chéry et Adeline Celier.

# Stack technique (Composants principaux)
- Infra:

- Infra:
- docker
- Backend:
- Kotlin
- Spring
- PostgreSQL
- Backend:
- Kotlin
- Spring
- PostgreSQL
- Geoserver
- Frontend:
- React (Create React App)
Expand All @@ -32,6 +34,7 @@ Le code source de monitorenv est fortement inspiré de [MonitorFish](https://git
# Installation de l'environnement de développement

## Prérequis

- npm
- openjdk
- docker + docker-compose
Expand All @@ -46,23 +49,26 @@ Modifier éventuellement `./infra/configurations/backend/application-dev.propert

`make dev-install` : installation des dépendances du Frontend
`make dev-run-front` : lance le serveur de développement du frontend
Le navigateur s'ouvre par défaut sur l'url http://localhost:3000
Le navigateur s'ouvre par défaut sur l'url <http://localhost:3000>

### Variables d'environnement

React CRA permet d'introduire des variables d'environnement au build.
Afin de permettre l'utilisation de variables d'environnement au run time de l'application, et d'éviter de compiler l'application frontend pour chaque environnement, les variables sont injectées dans l'application via le fichier `public/env.js` statique chargé par le client. Ce fichier est mis à jour avec les variables d'environnement via un script `env.sh`.
En développement, les variables d'environnement sont injectées via CRA. Le fichier `src/env.js` gère l'import des variables pour l'ensemble de l'application, quelque soit l'environnement (développemnet ou production).

## Backend

Vérifier la configuration : `make dev-back-config`
Lancer le backend :
`make dev-run-back-with-infra`:
- supprime éventuellemnt les précédentes instances docker
- crée une instance docker de la base de donnée + geoserver
- lance le serveur backend de développement
`make dev-run-back-with-infra`:

Le backend est alors accesible sur http://localhost:8880 par défaut.
Une interface Swagger est disponible sur l'url : http://localhost:8880/swagger-ui.html
- supprime éventuellemnt les précédentes instances docker
- crée une instance docker de la base de donnée + geoserver
- lance le serveur backend de développement

Le backend est alors accesible sur <http://localhost:8880> par défaut.
Une interface Swagger est disponible sur l'url : <http://localhost:8880/swagger-ui.html>

Il peut être utile de charger des données de contexte pour le développement du frontend, et de configurer Geoserver afin qu'il puisse distribuer ces données.

Expand All @@ -80,9 +86,19 @@ Installer les dépendances :

Installer les hooks de pre-commit
`cd datascience && poetry run pre-commit install`

# Clean Archi

## UseCases

- UseCases are HOF called with references to Repositories for inverted dependency injection
- UseCases references domain entities
- UseCases return domain entities

# Données

En production, certains jeux de données doivent être chargés manuellement :

- couches réglementaires : table `regulation_cacem`
- couches administratives : cf. migrations V0.001 à V0.033
- Moyens : table `control_resources`
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ package fr.gouv.cacem.monitorenv.domain.entities.controlThemes

data class ControlThemeEntity(
val id: Int,
val topic_level_1: String,
val topic_level_2: String? = null
val theme_level_1: String,
val theme_level_2: String? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class GetControlThemes(private val controlThemeRepository: IControlThemeReposito

fun execute(): List<ControlThemeEntity> {
val controlThemes = controlThemeRepository.findControlThemes()
logger.info("Found ${controlThemes.size} control topics ")
logger.info("Found ${controlThemes.size} control themes ")

return controlThemes
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import fr.gouv.cacem.monitorenv.domain.entities.controlThemes.ControlThemeEntity

data class ControlThemeDataOutput(
val id: Int,
val topic_level_1: String,
val topic_level_2: String? = null
val theme_level_1: String,
val theme_level_2: String? = null
) {
companion object {
fun fromControlThemeEntity(controlTheme: ControlThemeEntity) = ControlThemeDataOutput(
id = controlTheme.id,
topic_level_1 = controlTheme.topic_level_1,
topic_level_2 = controlTheme.topic_level_2
theme_level_1 = controlTheme.theme_level_1,
theme_level_2 = controlTheme.theme_level_2
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,27 @@ import fr.gouv.cacem.monitorenv.domain.entities.controlThemes.ControlThemeEntity
import javax.persistence.*

@Entity
@Table(name = "control_topics")
@Table(name = "control_themes")
data class ControlThemeModel(
@Id
@Column(name = "id")
var id: Int,
@Column(name = "topic_level_1")
var topic_level_1: String,
@Column(name = "topic_level_2")
var topic_level_2: String?
@Column(name = "theme_level_1")
var theme_level_1: String,
@Column(name = "theme_level_2")
var theme_level_2: String?
) {
fun toControlTheme() = ControlThemeEntity(
id = id,
topic_level_1 = topic_level_1,
topic_level_2 = topic_level_2
theme_level_1 = theme_level_1,
theme_level_2 = theme_level_2
)

companion object {
fun fromControlThemeEntity(controlTheme: ControlThemeEntity) = ControlThemeModel(
id = controlTheme.id,
topic_level_1 = controlTheme.topic_level_1,
topic_level_2 = controlTheme.topic_level_2
theme_level_1 = controlTheme.theme_level_1,
theme_level_2 = controlTheme.theme_level_2
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
ALTER TABLE IF EXISTS public.control_topics
RENAME TO control_themes;
ALTER TABLE public.control_themes RENAME COLUMN topic_level_1 TO theme_level_1;
ALTER TABLE public.control_themes RENAME COLUMN topic_level_2 TO theme_level_2;
ALTER SEQUENCE IF EXISTS public.control_topics_id_seq RENAME TO control_themes_id_seq;

UPDATE public.control_themes
SET theme_level_1 = 'Activités et manifestations soumises à évaluation d’incidence Natura 2000'
WHERE theme_level_1 ilike 'Activités et manifestations soumises à évaluation d’incidence Natura%';
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
TRUNCATE public.control_resources;
ALTER TABLE public.control_resources ALTER COLUMN administration SET NOT NULL;
ALTER TABLE public.control_resources ALTER COLUMN unit SET NOT NULL;
ALTER TABLE public.control_resources ADD UNIQUE (administration, unit, resource_name);
CREATE SEQUENCE public.control_resources_id_seq;
ALTER TABLE public.control_resources ALTER COLUMN id SET DEFAULT nextval('control_resources_id_seq');
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
INSERT INTO public.control_resources (administration, unit) VALUES ('DDTM', 'Cultures marines – DDTM 30');
INSERT INTO public.control_resources (administration, unit) VALUES ('DDTM', 'Cultures marines – DDTM 40');
INSERT INTO public.control_resources (administration, unit) VALUES ('DDTM', 'DML – DDTM 59');
INSERT INTO public.control_resources (administration, unit) VALUES ('DDTM', 'DML 2A');
INSERT INTO public.control_resources (administration, unit) VALUES ('DDTM', 'DPM – DDTM 14');
INSERT INTO public.control_resources (administration, unit) VALUES ('DDTM', 'DPM – DDTM 35');
INSERT INTO public.control_resources (administration, unit) VALUES ('DDTM', 'DPM – DDTM 44');
INSERT INTO public.control_resources (administration, unit) VALUES ('DDTM', 'SML 33');
INSERT INTO public.control_resources (administration, unit) VALUES ('DDTM', 'SML 50');
INSERT INTO public.control_resources (administration, unit) VALUES ('DDTM', 'Police de l''eau – DDTM 11');
INSERT INTO public.control_resources (administration, unit) VALUES ('DIRM DM', 'PAM Jeanne Barret');
INSERT INTO public.control_resources (administration, unit) VALUES ('DIRM DM', 'PAM Themis');
INSERT INTO public.control_resources (administration, unit) VALUES ('DIRM DM', 'Cross Etel');
INSERT INTO public.control_resources (administration, unit) VALUES ('DIRM DM', 'Cross Gris Nez');
INSERT INTO public.control_resources (administration, unit) VALUES ('Douane', 'BGC Ajaccio');
INSERT INTO public.control_resources (administration, unit) VALUES ('Douane', 'BGC Bastia');
INSERT INTO public.control_resources (administration, unit) VALUES ('Douane', 'BSN Ste Maxime');
INSERT INTO public.control_resources (administration, unit) VALUES ('Douane', 'DF 25 Libecciu');
INSERT INTO public.control_resources (administration, unit) VALUES ('DREAL', 'DREAL Pays-de-La-Loire');
INSERT INTO public.control_resources (administration, unit) VALUES ('Gendarmerie Maritime', 'P602 Verdon');
INSERT INTO public.control_resources (administration, unit) VALUES ('Gendarmerie Nationale', 'BN Toulon');
INSERT INTO public.control_resources (administration, unit) VALUES ('Gendarmerie Nationale', 'Brigade fluviale de Rouen');
INSERT INTO public.control_resources (administration, unit) VALUES ('Gestionnaire AMP', 'Natura 2000 Côte Bleue Marine');
INSERT INTO public.control_resources (administration, unit) VALUES ('Marine Nationale', 'A636 Maïto');
INSERT INTO public.control_resources (administration, unit) VALUES ('Office Français de la Biodiversité', 'OFB Sd 85');
INSERT INTO public.control_resources (administration, unit) VALUES ('Office Français de la Biodiversité', 'OFB SD974 Brigade Nature – SOI');
INSERT INTO public.control_resources (administration, unit) VALUES ('Parcs Naturels Régionaux', 'Parc Naturel Régional Martinique');
INSERT INTO public.control_resources (administration, unit) VALUES ('Parcs Nationaux', 'Parc National de Guadeloupe');
INSERT INTO public.control_resources (administration, unit) VALUES ('Parcs Naturels Marins', 'PNM Martinique');
INSERT INTO public.control_resources (administration, unit) VALUES ('Police Municipale', 'Police Municipale Le Marin 972');
INSERT INTO public.control_resources (administration, unit) VALUES ('Réserves Naturelles', 'Réserve Naturelle de L''Ilot M''Bouzi');
INSERT INTO public.control_resources (administration, unit) VALUES ('Réserves Naturelles', 'Réserve Naturelle 7 Iles');
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ class ControlThemesControllerITests {

val controlTheme = ControlThemeEntity(
id = 1,
topic_level_1 = "Police des mouillages",
topic_level_2 = "Mouillage individuel"
theme_level_1 = "Police des mouillages",
theme_level_2 = "Mouillage individuel"
)
given(this.getControlThemes.execute()).willReturn(listOf(controlTheme))

Expand All @@ -53,8 +53,8 @@ class ControlThemesControllerITests {
// Then
.andExpect(status().isOk)
.andExpect(jsonPath("$[0].id", equalTo(controlTheme.id)))
.andExpect(jsonPath("$[0].topic_level_1", equalTo(controlTheme.topic_level_1)))
.andExpect(jsonPath("$[0].topic_level_2", equalTo(controlTheme.topic_level_2)))
.andExpect(jsonPath("$[0].theme_level_1", equalTo(controlTheme.theme_level_1)))
.andExpect(jsonPath("$[0].theme_level_2", equalTo(controlTheme.theme_level_2)))
}

@Test
Expand All @@ -63,8 +63,8 @@ class ControlThemesControllerITests {

val controlTheme = ControlThemeEntity(
id = 1,
topic_level_1 = "Police des mouillages",
topic_level_2 = "Mouillage individuel"
theme_level_1 = "Police des mouillages",
theme_level_2 = "Mouillage individuel"
)

given(this.getControlThemeById.execute(3)).willReturn(controlTheme)
Expand All @@ -74,7 +74,7 @@ class ControlThemesControllerITests {
// Then
.andExpect(status().isOk)
.andExpect(jsonPath("$.id", equalTo(controlTheme.id)))
.andExpect(jsonPath("$.topic_level_1", equalTo(controlTheme.topic_level_1)))
.andExpect(jsonPath("$.topic_level_2", equalTo(controlTheme.topic_level_2)))
.andExpect(jsonPath("$.theme_level_1", equalTo(controlTheme.theme_level_1)))
.andExpect(jsonPath("$.theme_level_2", equalTo(controlTheme.theme_level_2)))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ class JpaControlThemeRepositoryITests : AbstractDBTests() {
val searchedControlTheme = ControlThemeModel.fromControlThemeEntity(
ControlThemeEntity(
id = 1,
topic_level_1 = "Police des mouillages",
topic_level_2 = "Mouillage individuel"
theme_level_1 = "Police des mouillages",
theme_level_2 = "Mouillage individuel"
)
)
// When
val requestedControlTheme = jpaControlThemesRepository.findControlThemeById(1)
// Then
assertThat(requestedControlTheme.id).isEqualTo(searchedControlTheme.id)
assertThat(requestedControlTheme.topic_level_1).isEqualTo(searchedControlTheme.topic_level_1)
assertThat(requestedControlTheme.topic_level_2).isEqualTo(searchedControlTheme.topic_level_2)
assertThat(requestedControlTheme.theme_level_1).isEqualTo(searchedControlTheme.theme_level_1)
assertThat(requestedControlTheme.theme_level_2).isEqualTo(searchedControlTheme.theme_level_2)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class JpaNatinfRepositoryITests : AbstractDBTests() {

@Test
@Transactional
fun `findNatinfs Should return all control topics`() {
fun `findNatinfs Should return all natinfs`() {
// When
val natinfs = jpaNatinfsRepository.findNatinfs()
assertThat(natinfs).hasSize(1056)
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/domain/shared_slices/Global.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@ const globalSlice = createSlice({
healthcheckTextWarning: null,
openedSideWindowTab: null,
sideWindowIsOpen: false,
// state entry for every component displayed on map
// state entry for every component displayed on map whose visibility should be controlled
displayLayersSidebar: true,
displayMissionsMenu: true,
displayMissionsOverlay: true,
displayMeasurement: true,
displayLocateOnMap: true,
displayInterestPoint: true,
displayDrawLayerModal: false
displayDrawLayerModal: false,
// state entry for every layer whose visibility should be controlled
displayMissionsLayer: true,
displaySelectedMissionLayer: true,
},
reducers: {
expandRightMenu (state) {
Expand Down
19 changes: 10 additions & 9 deletions frontend/src/domain/shared_slices/MissionsState.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,28 @@ const MissionStateReducer = null
const missionStateSlice = createSlice({
name: 'missionState',
initialState: {
// selectedMissionId on Map
selectedMissionId: null,
missionIdBeingEdited: null,
missionBeingEdited: null,
missionState: null
},
reducers: {
setSelectedMissionId (state, action) {
state.selectedMissionId = action.payload
},
setMissionIdBeingEdited (state, action) {
state.missionIdBeingEdited = action.payload
},
setMissionBeingEdited (state, action) {
state.missionBeingEdited = action.payload
resetSelectedMission (state) {
state.selectedMissionId = null
},
setMissionState (state, action) {
console.log('state updated', action.payload)
state.missionState = action.payload
}
}
})

export const {
setSelectedMissionId,
setMissionIdBeingEdited,
setMissionBeingEdited
resetSelectedMission,
setMissionState
} = missionStateSlice.actions

export default missionStateSlice.reducer
2 changes: 0 additions & 2 deletions frontend/src/domain/use_cases/missions/editMission.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ import { generatePath } from 'react-router'
import { setSideWindowPath } from '../../../components/SideWindowRouter/SideWindowRouter.slice'
import { sideWindowPaths, sideWindowMenu } from '../../entities/sideWindow'
import { openSideWindowTab } from '../../shared_slices/Global'
import { setMissionIdBeingEdited } from '../../shared_slices/MissionsState'



export const editMission = (missionId) => (dispatch) => {
dispatch(openSideWindowTab(sideWindowMenu.MISSIONS.code))
dispatch(setMissionIdBeingEdited(missionId))
dispatch(setSideWindowPath(generatePath(sideWindowPaths.MISSION, {id: missionId})))
}

Loading

0 comments on commit 71b92cc

Please sign in to comment.