Skip to content

Commit

Permalink
Saisie espèce libre (#18)
Browse files Browse the repository at this point in the history
* Rajout des icones et font pour le dsfr

* Premier jet de recherche d'espèce

* Finalisation de la saisie approximative des espèces
  • Loading branch information
DavidBruant authored Jun 17, 2024
1 parent b65fb42 commit 07ba590
Show file tree
Hide file tree
Showing 631 changed files with 820 additions and 26 deletions.
2 changes: 1 addition & 1 deletion .tsconfig.json → jsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"skipLibCheck": true,
"moduleResolution": "bundler",
"target": "esnext",
"lib": ["ES2022", "dom", "dom.iterable"],
"lib": ["es2022", "dom", "dom.iterable"],
"downlevelIteration": true,
"types": ["node"],
"noUnusedLocals": true,
Expand Down
30 changes: 30 additions & 0 deletions scripts/commun/manipulationStrings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//@ts-check

/**
* Normalisation du nom vernaculaire ou scientifique d'une seule espèce
* @param {string} nom
* @returns {string}
*/
export function normalizeNomEspèce(nom){
return nom
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '') // remove accents
.toLowerCase()
.replaceAll('(le)', '')
.replaceAll('(la)', '')
.replaceAll(`(l')`, '')
.trim()
}

/**
* Normalisation d'un texte long pouvant contenir des noms d'espèces
* @param {string} texte
* @returns {string}
*/
export function normalizeTexteEspèce(texte){
return texte
.trim()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '') // remove accents
.toLowerCase()
}
14 changes: 14 additions & 0 deletions scripts/commun/outils-espèces.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//@ts-check

/** @type {Map<ClassificationEtreVivant, (espèce: Espèce) => boolean>} */
export const filtreParClassification = new Map([
["oiseau", ((/** @type {Espèce} */ {REGNE, CLASSE}) => {
return REGNE === 'Animalia' && CLASSE === 'Aves'
})],
["faune non-oiseau", ((/** @type {Espèce} */ {REGNE, CLASSE}) => {
return REGNE === 'Animalia' && CLASSE !== 'Aves'
})],
["flore", ((/** @type {Espèce} */ {REGNE}) => {
return REGNE === 'Plantae'
})]
])
10 changes: 5 additions & 5 deletions scripts/commun/typeFormat.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ function recoverDate(d){
*/
export function normalizeNomCommune(nomCommune) {
return nomCommune
.replace(/-|'/g, ' ')
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '') // remove accent because GH pages triggers file download
.toLowerCase()
}
.replace(/-|'/g, ' ')
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '') // remove accents
.toLowerCase()
}


/**
Expand Down
11 changes: 11 additions & 0 deletions scripts/front-end/components/AutocompleteEspèces.svelte
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
<script>
//@ts-check
import AutoComplete from "simple-svelte-autocomplete"
/** @type {Espèce[]} */
export let espèces;
export let selectedItem = undefined;
export let onChange = undefined
export let htmlClass
$: espèceToLabel = makeEspèceToLabel(espèces)
/**
*
* @param {Espèce} esp
*/
function espèceLabel(esp){
return `${esp["NOM_VERN"]} (${esp["LB_NOM"]})`
}
/**
*
* @param {Espèce[]} espèces
*/
function makeEspèceToLabel(espèces){
return new Map(espèces.map(e => [e, espèceLabel(e)]))
}
Expand Down
148 changes: 142 additions & 6 deletions scripts/front-end/components/SaisieEspèces.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@
//@ts-check
import Squelette from './Squelette.svelte'
import AutocompleteEspeces from "./AutocompleteEspèces.svelte"
import {normalizeNomEspèce, normalizeTexteEspèce} from '../../commun/manipulationStrings.js'
import {filtreParClassification} from '../../commun/outils-espèces.js'
import '../../types.js'
/** @type {Map<ClassificationEtreVivant, Espèce[]>} */
export let espècesProtégéesParClassification;
export let activitesParClassificationEtreVivant
Expand All @@ -13,8 +19,6 @@
/** @type { DescriptionMenaceEspèce[] } */
export let descriptionMenacesEspèces;
console.log('descriptionMenacesEspèces', descriptionMenacesEspèces)
const mailto = "mailto:[email protected]?subject=Rajouter%20une%20esp%C3%A8ce%20prot%C3%A9g%C3%A9e%20manquante&body=Bonjour%2C%0D%0A%0D%0AJe%20souhaite%20saisir%20une%20esp%C3%A8ce%20prot%C3%A9g%C3%A9es%20qui%20n'est%20pas%20list%C3%A9e%20dans%20l'outil%20Pitchou.%0D%0AFiche%20descriptive%20de%20l'esp%C3%A8ce%20%3A%0D%0A%0D%0ANom%20vernaculaire%20%3A%0D%0ANom%20latin%20%3A%0D%0ACD_NOM%20(identifiant%20TaxRef)%20%3A%0D%0ACommentaire%20%3A%0D%0A%0D%0AJe%20vous%20remercie%20de%20bien%20vouloir%20ajouter%20cette%20esp%C3%A8ce%0D%0A%0D%0AJe%20vous%20souhaite%20une%20belle%20journ%C3%A9e%20%E2%98%80%EF%B8%8F"
/**
Expand Down Expand Up @@ -43,7 +47,7 @@
return btoa(unescape(encodeURIComponent(s)))
}
/** @type {Map<ClassificationEtreVivant, any>} */
const etreVivantClassificationToBloc = new Map([
["oiseau", {
sectionClass: "saisie-oiseau",
Expand All @@ -60,7 +64,15 @@
])
function ajouterEspèce(espèce, classification, etresVivantsAtteints){
/**
*
* @param {Espèce} espèce
* @param {ClassificationEtreVivant} classification
*/
function ajouterEspèce(espèce, classification){
//@ts-expect-error La description pour la classification va être trouvée
const etresVivantsAtteints = descriptionMenacesEspèces.find(d => d.classification === classification).etresVivantsAtteints
if(classification === 'oiseau'){
etresVivantsAtteints.push({
espece: espèce,
Expand Down Expand Up @@ -94,7 +106,12 @@
}
return 0;
}
/**
*
* @param {EtreVivantAtteint} etresVivantsAtteints
* @param {Espèce} _espece
*/
function supprimerLigne(etresVivantsAtteints, _espece){
const index = etresVivantsAtteints.findIndex(({espece}) => espece === _espece);
if (index > -1) {
Expand Down Expand Up @@ -155,6 +172,7 @@
})
}
/** @type {HTMLButtonElement} */
let copyButton;
let lienPartage;
Expand All @@ -178,12 +196,93 @@
}
/**
* Recheche "à l'arrache"
*/
/**
*
* @param {Map<ClassificationEtreVivant, Espèce[]>} espècesProtégéesParClassification
* @returns {Map<string, Espèce>}
*/
function créerNomVersEspèceClassif(espècesProtégéesParClassification){
/** @type {Map<string, Espèce>}>} */
const nomVersEspèceClassif = new Map()
for(const espèces of espècesProtégéesParClassification.values()){
for(const espèce of espèces){
const {NOM_VERN, LB_NOM} = espèce;
if(LB_NOM && LB_NOM.length >= 3){
const normalized = normalizeNomEspèce(LB_NOM)
nomVersEspèceClassif.set(normalized, espèce)
}
if(NOM_VERN){
const noms = NOM_VERN.split(',')
for(const nom of noms){
const normalized = normalizeNomEspèce(nom)
if(normalized && normalized.length >= 3){
nomVersEspèceClassif.set(normalized, espèce)
}
}
}
}
}
return nomVersEspèceClassif
}
let texteEspèces = '';
$: nomVersEspèceClassif = créerNomVersEspèceClassif(espècesProtégéesParClassification)
/**
*
* @param {string} texte
* @returns {Set<Espèce>}
*/
function chercherEspèces(texte){
/** @type {Set<Espèce>}*/
const espècesTrouvées = new Set()
for(const [nom, espClassif] of nomVersEspèceClassif){
if(texte.includes(nom)){
espècesTrouvées.add(espClassif)
}
}
return espècesTrouvées
}
/** @type {Set<Espèce> | undefined} */
$: espècesÀPréremplir = chercherEspèces(normalizeTexteEspèce(texteEspèces))
/**
* @param {Set<Espèce>} _espècesÀPréremplir
*/
function préremplirFormulaire(_espècesÀPréremplir){
for(const espèce of _espècesÀPréremplir){
// PPP pas une manière super-efficace de récupérer
for(const [classification, filtre] of filtreParClassification){
if(filtre(espèce)){
ajouterEspèce(espèce, classification)
continue;
}
}
}
texteEspèces = ''
}
</script>


<Squelette nav={false}>
<article>
<div class="fr-grid-row fr-pt-6w fr-grid-row--center">
<div class="fr-grid-row fr-mt-6w">
<div class="fr-col">
<h1>Saisie des espèces protégées impactées</h1>

Expand All @@ -193,6 +292,35 @@
</section>
</div>
</div>

<div class="fr-grid-row fr-mt-6w">
<div class="fr-col">
<details>
<summary><h2>Saisie approximative</h2></summary>
<section>
<p>
Dans la boîte de texte ci-dessous, coller du texte approximatif.
Par exemple, en copiant à partir d'un tableau dans un pdf, ou une liste d'espèces qui trainent.
Les espèces seront reconnues et permettront le pré-remplissage du formulaire
</p>
<textarea bind:value={texteEspèces} class="fr-input"></textarea>
</section>
{#if espècesÀPréremplir && espècesÀPréremplir.size >= 1}
<section>
<h3>{espècesÀPréremplir.size} espèce.s trouvée.s dans le texte</h3>
<ul>
{#each [...espècesÀPréremplir] as espèce}
<li>{espèce["NOM_VERN"]} (<i>{espèce["LB_NOM"]}</i>)</li>
{/each}
</ul>

<button on:click={() => préremplirFormulaire(espècesÀPréremplir)} type="button" class="fr-btn">Pré-remplir avec ces espèces</button>
</section>
{/if}
</details>
</div>
</div>

<form>
{#each descriptionMenacesEspèces as {classification, etresVivantsAtteints}}
<div class="fr-grid-row fr-pt-6w fr-grid-row--center">
Expand Down Expand Up @@ -321,6 +449,14 @@
<style lang="scss">
article{
details{
cursor: default; // surcharge dsfr parce que c'est bizarre
}
summary h2{
display: inline-block;
}
.saisie-oiseau, .saisie-flore, .saisie-faune {
display: flex;
flex-direction: column;
Expand Down
20 changes: 8 additions & 12 deletions scripts/front-end/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ import {init, secretFromURL, logout, chargerDossiers} from './actions/main.js'
import {envoiEmailConnexion} from './serveur.js'

import { authorizedEmailDomains } from '../commun/constantes.js';
import { normalizeNomCommune } from '../commun/typeFormat.js';
import {filtreParClassification} from '../commun/outils-espèces.js'

import '../types.js'
import { normalizeNomCommune } from '../commun/typeFormat.js';


const svelteTarget = document.querySelector('.svelte-main')

Expand Down Expand Up @@ -106,6 +108,11 @@ page('/saisie-especes', async () => {
return classificationEtreVivants.includes(x)
}

/**
*
* @param {string} selector
* @returns {string}
*/
function getURL(selector){
const element = document.head.querySelector(selector)

Expand Down Expand Up @@ -206,17 +213,6 @@ page('/saisie-especes', async () => {
/** @type { Espèce[] } */
const listeEspècesProtégées = [...espèceByCD_NOM.values()]

const filtreParClassification = new Map([
["oiseau", ((/** @type {{REGNE: Règne, CLASSE: Classe}} */ {REGNE, CLASSE}) => {
return REGNE === 'Animalia' && CLASSE === 'Aves'
})],
["faune non-oiseau", ((/** @type {{REGNE: Règne, CLASSE: Classe}} */ {REGNE, CLASSE}) => {
return REGNE === 'Animalia' && CLASSE !== 'Aves'
})],
["flore", ((/** @type {{REGNE: Règne, CLASSE: Classe}} */ {REGNE}) => {
return REGNE === 'Plantae'
})]
])

const espècesProtégéesParClassification = new Map(
[...filtreParClassification].map(([classif, filtre]) => ([classif, listeEspècesProtégées.filter(filtre)]))
Expand Down
5 changes: 3 additions & 2 deletions scripts/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@
* @typedef {Object} Espèce
* @prop { string } CD_NOM // pérennité ?
* @prop { string } NOM_VERN
* @prop { Règne } CLASSE
* @prop { Classe } REGNE
* @prop { string } LB_NOM
* @prop { Classe } CLASSE
* @prop { Règne } REGNE
*
*/

Expand Down
Binary file added style/dsfr/fonts/Marianne-Bold.woff
Binary file not shown.
Binary file added style/dsfr/fonts/Marianne-Bold.woff2
Binary file not shown.
Binary file added style/dsfr/fonts/Marianne-Bold_Italic.woff
Binary file not shown.
Binary file added style/dsfr/fonts/Marianne-Bold_Italic.woff2
Binary file not shown.
Binary file added style/dsfr/fonts/Marianne-Light.woff
Binary file not shown.
Binary file added style/dsfr/fonts/Marianne-Light.woff2
Binary file not shown.
Binary file added style/dsfr/fonts/Marianne-Light_Italic.woff
Binary file not shown.
Binary file added style/dsfr/fonts/Marianne-Light_Italic.woff2
Binary file not shown.
Binary file added style/dsfr/fonts/Marianne-Medium.woff
Binary file not shown.
Binary file added style/dsfr/fonts/Marianne-Medium.woff2
Binary file not shown.
Binary file added style/dsfr/fonts/Marianne-Medium_Italic.woff
Binary file not shown.
Binary file added style/dsfr/fonts/Marianne-Medium_Italic.woff2
Binary file not shown.
Binary file added style/dsfr/fonts/Marianne-Regular.woff
Binary file not shown.
Binary file added style/dsfr/fonts/Marianne-Regular.woff2
Binary file not shown.
Binary file added style/dsfr/fonts/Marianne-Regular_Italic.woff
Binary file not shown.
Binary file added style/dsfr/fonts/Marianne-Regular_Italic.woff2
Binary file not shown.
Binary file added style/dsfr/fonts/Spectral-ExtraBold.woff
Binary file not shown.
Binary file added style/dsfr/fonts/Spectral-ExtraBold.woff2
Binary file not shown.
Binary file added style/dsfr/fonts/Spectral-Regular.woff
Binary file not shown.
Binary file added style/dsfr/fonts/Spectral-Regular.woff2
Binary file not shown.
1 change: 1 addition & 0 deletions style/dsfr/icons/buildings/ancient-gate-fill.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions style/dsfr/icons/buildings/ancient-gate-line.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions style/dsfr/icons/buildings/ancient-pavilion-fill.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions style/dsfr/icons/buildings/ancient-pavilion-line.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions style/dsfr/icons/buildings/bank-fill.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions style/dsfr/icons/buildings/bank-line.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions style/dsfr/icons/buildings/building-fill.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions style/dsfr/icons/buildings/building-line.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 07ba590

Please sign in to comment.