diff --git a/nupes-elections-front/src/App.js b/nupes-elections-front/src/App.js
index fbe4eb8..f5c3c61 100644
--- a/nupes-elections-front/src/App.js
+++ b/nupes-elections-front/src/App.js
@@ -1,22 +1,39 @@
import {createBrowserRouter, RouterProvider} from "react-router-dom"
import './App.css'
-import Elections2024 from './Elections2024'
+import ElectionsLegislatives2022 from "./ElectionsLegislatives2022"
+import ElectionsEuropeennes2024 from './ElectionsEuropeennes2024'
function App() {
const router = createBrowserRouter([
{
path: "/",
- element: ,
+ element: ,
+ },
+ {
+ path: "/elections/legislatives/2022/:typeResultats/",
+ element: ,
+ },
+ {
+ path: "/elections/legislatives/2022/:typeResultats/:zoneId/",
+ element:
},
{
path: "/elections/europeennes/2024/:typeResultats/",
- element: ,
+ element: ,
},
{
path: "/elections/europeennes/2024/:typeResultats/:zoneId/",
- element:
+ element:
},
+ // {
+ // path: "/elections/legislatives/2024/:typeResultats/",
+ // element: ,
+ // },
+ // {
+ // path: "/elections/legislatives/2024/:typeResultats/:zoneId/",
+ // element:
+ // },
])
return <>
diff --git a/nupes-elections-front/src/ElectionsEuropeennes2024.js b/nupes-elections-front/src/ElectionsEuropeennes2024.js
index c68dca3..6e9651e 100644
--- a/nupes-elections-front/src/ElectionsEuropeennes2024.js
+++ b/nupes-elections-front/src/ElectionsEuropeennes2024.js
@@ -63,6 +63,27 @@ export default function ElectionsEuropeennes2024() {
const siegesParListe = calculerSieges(listes, donnees, retirerSeuil ? 0 : 0.05)
const [siegesParBloc, siegesParNuance] = regrouperVoix(siegesParListe, listes, blocs, nuances)
+ const [categoriesHistogramme, valeursHistogramme, couleursHistogramme] = useMemo(() => {
+ if (grouperParBloc) {
+ const categories = {}
+ const couleurs = {}
+ for (let bloc of blocs) {
+ categories[bloc.nom] = bloc.nom
+ couleurs[bloc.nom] = bloc.couleur
+ }
+ return [categories, voixParBloc, couleurs]
+ }
+ else {
+ const categories = {}
+ const couleurs = {}
+ for (let nuance of nuances) {
+ categories[nuance.code] = nuance.nom
+ couleurs[nuance.code] = nuance.couleur
+ }
+ return [categories, voixParNuance, couleurs]
+ }
+ }, [typeResultats, nuances, blocs, donnees, voixParNuance, voixParBloc, grouperParBloc])
+
return <>
@@ -73,8 +94,8 @@ export default function ElectionsEuropeennes2024() {
+ nomCategories={categoriesHistogramme} valeurParCategorie={valeursHistogramme}
+ totalExprimes={donnees.exprimes} couleurParCategorie={couleursHistogramme} />
diff --git a/nupes-elections-front/src/ElectionsLegislatives2022.js b/nupes-elections-front/src/ElectionsLegislatives2022.js
new file mode 100644
index 0000000..5bdca16
--- /dev/null
+++ b/nupes-elections-front/src/ElectionsLegislatives2022.js
@@ -0,0 +1,148 @@
+import {useParams} from "react-router-dom"
+import {AppBar, Container, Toolbar} from "@mui/material"
+import * as Highcharts from 'highcharts'
+import highchartsItem from 'highcharts/modules/item-series'
+import {useEffect, useMemo, useState} from "react"
+import {
+ SelectionAffichage,
+ TableauParticipation,
+ CarteResultats,
+ HistogrammeVoix, GroupementParBloc, SelectionTour
+} from "./includes/composants_elections"
+import {getNomZone, regrouperVoix} from "./utils"
+import 'leaflet/dist/leaflet.css'
+import {
+ TableauResultatsCandidatsLegislatives,
+ TableauResultatsNuancesLegislatives
+} from "./includes/composants_elections_legislatives"
+
+
+highchartsItem(Highcharts)
+
+export default function ElectionsLegislatives2022() {
+ const {typeResultats, zoneId} = useParams()
+
+ const [grouperParBloc, setGrouperParBloc] = useState(false)
+ const [tour, setTour] = useState(1)
+ const [blocs, setBlocs] = useState([])
+ const [candidats, setCandidats] = useState([])
+ const [nuances, setNuances] = useState([])
+ const [resultats, setResultats] = useState([])
+ const [typeSousZone, setTypeSousZone] = useState("region")
+
+ useEffect(() => {
+ fetch("/data/resultats/legislatives/2022/blocs.json").then(response => response.json())
+ .then(data => setBlocs(data))
+ fetch("/data/resultats/legislatives/2022/nuances.json").then(response => response.json())
+ .then(data => setNuances(data))
+
+ if (typeResultats === "france") {
+ fetch("/data/resultats/legislatives/2022/france.json").then(response => response.json())
+ .then(data => setResultats(data))
+ }
+ else {
+ fetch(`/data/resultats/legislatives/2022/${typeResultats}/${zoneId}.json`)
+ .then(response => response.json())
+ .then(data => setResultats(data))
+ }
+ }, [typeResultats, zoneId])
+
+ const zoneInfo = useMemo(() => resultats?.zone ?? {}, [resultats])
+ const nomZone = useMemo(() => getNomZone(typeResultats, zoneInfo), [typeResultats, zoneInfo])
+
+ useEffect(() => {
+ let circonscription = ""
+
+ if (typeResultats === "circonscription")
+ circonscription = zoneId
+ else if (typeResultats === "bureau_vote")
+ circonscription = zoneInfo?.circonscription ?? ""
+ else {
+ setCandidats(nuances)
+ }
+
+ if (!circonscription)
+ return
+
+ fetch(`/data/resultats/legislatives/2022/candidats/${circonscription}.json`)
+ .then(response => response.json())
+ .then(data => setCandidats(data))
+ }, [typeResultats, zoneId, zoneInfo, nuances])
+
+ const donnees = useMemo(() => {
+ if (tour === 1)
+ return resultats?.tour1 ?? {}
+ else if (tour === 2)
+ return resultats?.tour2 ?? {}
+ else
+ return {}
+ }, [resultats, tour])
+
+ const dejaGroupeParNuance = typeResultats !== "circonscription" && typeResultats !== "bureau_vote"
+ const [voixParBloc, voixParNuance] = regrouperVoix(donnees.voix, candidats, blocs, nuances,
+ dejaGroupeParNuance)
+
+ const candidatKey = typeSousZone === "circonscription" || typeSousZone === "bureau_vote" ? "numero" : "code"
+
+ const [categoriesHistogramme, valeursHistogramme, couleursHistogramme] = useMemo(() => {
+ if (grouperParBloc) {
+ const categories = {}
+ const couleurs = {}
+ for (let bloc of blocs) {
+ categories[bloc.nom] = bloc.nom
+ couleurs[bloc.nom] = bloc.couleur
+ }
+ return [categories, voixParBloc, couleurs]
+ }
+ else {
+ if (typeResultats === "circonscription" || typeResultats === "bureau_vote") {
+ // On affiche les noms des candidat⋅es
+ const categories = {}
+ const couleurs = {}
+ for (let candidat of candidats) {
+ categories[candidat.numero] = `${candidat.prenom} ${candidat.nom} (${candidat.nuance})`
+ couleurs[candidat.numero] = nuances.filter(nuance => nuance.code === candidat.nuance)[0]?.couleur
+ }
+ return [categories, donnees.voix, couleurs]
+ }
+ else {
+ // On affiche les nuances
+ const categories = {}
+ const couleurs = {}
+ for (let nuance of nuances) {
+ categories[nuance.code] = nuance.nom
+ couleurs[nuance.code] = nuance.couleur
+ }
+ return [categories, voixParNuance, couleurs]
+
+ }
+ }
+ }, [typeResultats, candidats, nuances, blocs, donnees, voixParNuance, voixParBloc, grouperParBloc])
+
+ const tableauResultats = useMemo(() => {
+ if (typeResultats === "circonscription" || typeResultats === "bureau_vote")
+ return
+ else
+ return
+ }, [typeResultats, candidats, blocs, nuances, donnees])
+
+ return <>
+
+
+
+
+
+
+
+
+
+ {tableauResultats}
+
+
+
+ >
+}
diff --git a/nupes-elections-front/src/includes/composants_elections.js b/nupes-elections-front/src/includes/composants_elections.js
index aaa3a4e..c3b689d 100644
--- a/nupes-elections-front/src/includes/composants_elections.js
+++ b/nupes-elections-front/src/includes/composants_elections.js
@@ -16,25 +16,19 @@ import Switch from "@mui/material/Switch";
import FormControlLabel from "@mui/material/FormControlLabel";
-export function HistogrammeVoix({titre, resultats, voixParNuance, voixParBloc, blocs, nuances, grouperParBloc}) {
+export function HistogrammeVoix({titre, nomCategories, valeurParCategorie, totalExprimes, couleurParCategorie}) {
const [categoriesVoix, dataVoix] = useMemo(() => {
const categories = []
const data = []
- if (grouperParBloc) {
- for (let bloc of blocs) {
- categories.push(bloc.nom)
- data.push([bloc.nom, voixParBloc[bloc.nom], bloc.couleur, bloc.nom])
- }
- }
- else {
- for (let nuance of nuances) {
- categories.push(nuance.nom)
- data.push([nuance.nom, voixParNuance[nuance.code], nuance.couleur, nuance.nom])
- }
+
+ for (let categorie of Object.keys(nomCategories)) {
+ categories.push(nomCategories[categorie])
+ data.push([nomCategories[categorie], valeurParCategorie[categorie], couleurParCategorie[categorie],
+ nomCategories[categorie]])
}
return [categories, data]
- }, [voixParBloc, voixParNuance, blocs, nuances, grouperParBloc])
+ }, [nomCategories, valeurParCategorie, couleurParCategorie])
const scoreOptions = {
chart: {
@@ -45,7 +39,7 @@ export function HistogrammeVoix({titre, resultats, voixParNuance, voixParBloc, b
},
tooltip: {
formatter: function () {
- return `${this.x} : ${this.y} voix (${(100 * this.y / resultats.exprimes).toFixed(2)} %)
`
+ return `${this.x} : ${this.y} voix (${(100 * this.y / totalExprimes).toFixed(2)} %)
`
}
},
xAxis: {
@@ -244,9 +238,11 @@ export function SelectionAffichage({typeResultats, typeSousZone, setTypeSousZone
function ZoneGeoJSON({typeElection, anneeElection, resultatsZone, typeSousZone,
- candidats, blocs, nuances, tour, grouperParBloc = false}) {
+ candidats, blocs, nuances, tour, grouperParBloc = false, candidatKey = "numero"}) {
const sousZoneInfo = resultatsZone.zone
- const donnees = resultatsZone ? (tour === 1 ? resultatsZone.tour1 : resultatsZone.tour2) : {}
+ const donnees = useMemo(() => {
+ return resultatsZone ? (tour === 1 ? resultatsZone.tour1 : resultatsZone.tour2) : {}
+ }, [resultatsZone, tour])
const [idZone, nomZone] = useMemo(() => {
if (!sousZoneInfo)
@@ -262,10 +258,23 @@ function ZoneGeoJSON({typeElection, anneeElection, resultatsZone, typeSousZone,
return ["", ""]
}, [typeSousZone, sousZoneInfo])
- const voixCandidats = useMemo(() => donnees?.voix ?? {}, [resultatsZone])
- const candidatsTries = trierCandidats(candidats, voixCandidats)
+ const [candidatsZone, setCandidatsZone] = useState(candidats)
+ useEffect(() => {
+ if (typeElection === "legislatives" && (typeSousZone === "circonscription" || typeSousZone === "bureau_vote") && sousZoneInfo.id) {
+ const circo_id = typeSousZone === "circonscription" ? sousZoneInfo.id : sousZoneInfo.circonscription
+ fetch(`/data/resultats/${typeElection}/${anneeElection}/candidats/${circo_id}.json`)
+ .then(response => response.json())
+ .then(data => setCandidatsZone(data))
+ }
+ }, [typeElection, anneeElection, typeSousZone, sousZoneInfo])
- const [voixParBloc, voixParNuance] = regrouperVoix(voixCandidats, candidats, blocs, nuances)
+ const voixCandidats = useMemo(() => donnees?.voix ?? {}, [donnees])
+ const candidatsTries = trierCandidats(candidatsZone, voixCandidats, candidatKey)
+
+ const dejaGroupesParNuance = typeElection === "legislatives"
+ && (typeSousZone !== "circonscription" && typeSousZone !== "bureau_vote")
+ const [voixParBloc, voixParNuance] = regrouperVoix(voixCandidats, candidatsZone, blocs, nuances,
+ dejaGroupesParNuance)
let couleur = 'grey'
if (grouperParBloc) {
@@ -294,8 +303,8 @@ function ZoneGeoJSON({typeElection, anneeElection, resultatsZone, typeSousZone,
{nomZone}
{candidatsTries.slice(0, 5).map(candidat =>
- -
- {candidat.nom} : {voixCandidats[candidat.numero]} ({(100 * voixCandidats[candidat.numero] / donnees.exprimes).toFixed(2)} %)
+
-
+ {candidat.nom} : {voixCandidats[candidat[candidatKey]]} ({(100 * voixCandidats[candidat[candidatKey]] / donnees.exprimes).toFixed(2)} %)
)}
@@ -303,7 +312,7 @@ function ZoneGeoJSON({typeElection, anneeElection, resultatsZone, typeSousZone,
}
function ContenuCarte({typeElection, anneeElection, zoneInfo, typeSousZone, candidats, blocs, nuances, tour,
- grouperParBloc = false}) {
+ grouperParBloc = false, candidatKey = "numero"}) {
const map = useMap()
const [resultatsZones, setResultatsZones] = useState([])
@@ -366,15 +375,15 @@ function ContenuCarte({typeElection, anneeElection, zoneInfo, typeSousZone, cand
return <>
{resultatsZones.filter(resultatsZone => resultatsZone.zone.geometry['type'])
.map(resultatsZone =>
- )}
+ blocs={blocs} nuances={nuances} tour={tour} grouperParBloc={grouperParBloc} candidatKey={candidatKey} />)}
>
}
export function CarteResultats({typeElection, anneeElection, typeResultats, zoneInfo, typeSousZone, candidats,
- blocs, nuances, tour, grouperParBloc = false}) {
+ blocs, nuances, tour, grouperParBloc = false, candidatKey = "numero"}) {
const center = typeResultats === "france" ? [46.603354, 1.888334] : [0, 0]
return <>
@@ -385,7 +394,7 @@ export function CarteResultats({typeElection, anneeElection, typeResultats, zone
/>
+ blocs={blocs} nuances={nuances} tour={tour} grouperParBloc={grouperParBloc} candidatKey={candidatKey} />
>
}
diff --git a/nupes-elections-front/src/includes/composants_elections_legislatives.js b/nupes-elections-front/src/includes/composants_elections_legislatives.js
new file mode 100644
index 0000000..f12b752
--- /dev/null
+++ b/nupes-elections-front/src/includes/composants_elections_legislatives.js
@@ -0,0 +1,103 @@
+import {trierCandidats} from "../utils"
+import TableContainer from "@mui/material/TableContainer"
+import Paper from "@mui/material/Paper"
+import Table from "@mui/material/Table"
+import TableHead from "@mui/material/TableHead"
+import TableRow from "@mui/material/TableRow"
+import TableCell from "@mui/material/TableCell"
+import TableBody from "@mui/material/TableBody"
+
+export function TableauResultatsCandidatsLegislatives({blocs, candidats, nuances, donnees}) {
+ const voixCandidats = donnees?.voix ?? {}
+ const candidatsTriees = trierCandidats(candidats, voixCandidats)
+
+ return <>
+
+
+
+
+ Numéro
+ Candidat
+ Nuance
+ Bloc
+ Voix
+ % Inscrit⋅es
+ % Exprimé⋅es
+
+
+
+ {candidatsTriees.map((candidat) => (
+
+ ))}
+
+
+
+ >
+}
+
+function LigneCandidat({candidat, voix, donnees, nuances, blocs}) {
+ const nuance = nuances.filter(nuance => nuance.code === candidat.nuance)[0]
+ const bloc = blocs.filter(bloc => bloc.nom === candidat.bloc)[0]
+
+ return
+ {candidat.numero}
+ {candidat.prenom} {candidat.nom}
+
+ {nuance.nom} ({nuance.code})
+
+ {bloc.nom}
+ {voix}
+ {(100 * voix / donnees.inscrits).toFixed(2)} %
+ {(100 * voix / donnees.exprimes).toFixed(2)} %
+
+}
+
+/**
+ * Composant pour le tableau des résultats des élections législatives
+ * @param blocs
+ * @param nuances
+ * @param donnees
+ * @return {JSX.Element}
+ * @constructor
+ */
+export function TableauResultatsNuancesLegislatives({blocs, nuances, donnees}) {
+ const voixNuances = donnees?.voix ?? {}
+ const nuancesTriees = trierCandidats(nuances, voixNuances, "code")
+
+ return <>
+
+
+
+
+ Nuance
+ Bloc
+ Voix
+ % Inscrit⋅es
+ % Exprimé⋅es
+
+
+
+ {nuancesTriees.map((nuance) => (
+
+ ))}
+
+
+
+ >
+}
+
+function LigneNuance({nuance, voix, donnees, blocs}) {
+ const bloc = blocs.filter(bloc => bloc.nom === nuance.bloc)[0]
+
+ return
+
+ {nuance.nom} ({nuance.code})
+
+ {bloc.nom}
+ {voix}
+ {(100 * voix / donnees.inscrits).toFixed(2)} %
+ {(100 * voix / donnees.exprimes).toFixed(2)} %
+
+}
diff --git a/nupes-elections-front/src/utils.js b/nupes-elections-front/src/utils.js
index d6e4a95..aa6aefd 100644
--- a/nupes-elections-front/src/utils.js
+++ b/nupes-elections-front/src/utils.js
@@ -1,5 +1,5 @@
export function getNomZone(typeResultats, zoneInfo) {
- if (!zoneInfo)
+ if (!zoneInfo.type)
return ""
else if (typeResultats === "france")
return "France"
@@ -15,30 +15,36 @@ export function getNomZone(typeResultats, zoneInfo) {
return zoneInfo.libelle
}
-export function trierCandidats(candidats, voix_par_candidat) {
+export function trierCandidats(candidats, voix_par_candidat, key = "numero") {
return candidats.toSorted((l1, l2) => {
- return (voix_par_candidat[l2.numero] || 0) - (voix_par_candidat[l1.numero] || 0)
+ return (voix_par_candidat[l2[key]] || 0) - (voix_par_candidat[l1[key]] || 0)
})
}
-export function regrouperVoix(voixCandidats, candidats, blocs, nuances) {
+export function regrouperVoix(voixCandidats, candidats, blocs, nuances, dejaGroupesParNuance = false) {
if (!candidats || !voixCandidats || !blocs || !nuances
|| candidats.length === 0 || blocs.length === 0 || nuances.length === 0)
return [{}, {}]
+ const key = dejaGroupesParNuance ? "code" : "numero"
+
const parBloc = {}
- const parNuance = {}
+ const parNuance = dejaGroupesParNuance ? voixCandidats : {}
for (let bloc of blocs) {
parBloc[bloc.nom] = 0
}
- for (let nuance of nuances) {
- parNuance[nuance.code] = 0
+
+ if (!dejaGroupesParNuance) {
+ for (let nuance of nuances) {
+ parNuance[nuance.code] = 0
+ }
}
for (let candidat of candidats) {
- parBloc[candidat.bloc] += voixCandidats[candidat.numero] || 0
- parNuance[candidat.nuance] += voixCandidats[candidat.numero] || 0
+ parBloc[candidat.bloc] += voixCandidats[candidat[key]] || 0
+ if (!dejaGroupesParNuance)
+ parNuance[candidat.nuance] += voixCandidats[candidat[key]] || 0
}
return [parBloc, parNuance]
diff --git a/nupes/scripts/legislatives2022/export_resultats.py b/nupes/scripts/legislatives2022/export_resultats.py
index 045034b..ddc1f2b 100644
--- a/nupes/scripts/legislatives2022/export_resultats.py
+++ b/nupes/scripts/legislatives2022/export_resultats.py
@@ -31,7 +31,12 @@ def exporter_nuances(engine: Engine, verbose: bool = False) -> None:
nuances_json = []
for nuance in nuances:
- nuance_json = {'code': nuance.code, 'nom': nuance.nom, 'couleur': nuance.couleur, "bloc": nuance.bloc_id}
+ nuance_json = {
+ 'code': nuance.code,
+ 'nom': nuance.nom,
+ 'couleur': nuance.couleur,
+ "bloc": nuance.bloc.nom,
+ }
nuances_json.append(nuance_json)
file = DATA_DIR / "resultats" / "legislatives" / "2022" / "nuances.json"
@@ -54,6 +59,7 @@ def exporter_candidats(engine: Engine, verbose: bool = False) -> None:
candidat_json = {
'numero': candidat.numero,
'nuance': candidat.nuance_id,
+ 'bloc': candidat.nuance.bloc.nom,
'nom': candidat.nom,
'prenom': candidat.prenom,
'nom_suppleance': candidat.nom_suppleance,
@@ -316,8 +322,8 @@ def exporter_resultats_circonscriptions(engine: Engine, verbose: bool = False) -
resultats_dict['tour1']['voix'] = resultats_t1
resultats_dict['tour2']['voix'] = resultats_t2
for voix_candidat in resultats_circonscription.voix:
- resultats_t1[voix_candidat.candidat.id] = voix_candidat.voix_t1
- resultats_t2[voix_candidat.candidat.id] = voix_candidat.voix_t2
+ resultats_t1[voix_candidat.candidat.numero] = voix_candidat.voix_t1
+ resultats_t2[voix_candidat.candidat.numero] = voix_candidat.voix_t2
file = DATA_DIR / "resultats" / "legislatives" / "2022" / "circonscription" / f"{circonscription.id}.json"
if not file.parent.is_dir():
@@ -457,8 +463,8 @@ def exporter_resultats_bureaux_vote(engine: Engine, verbose: bool = False) -> No
resultats_dict['tour1']['voix'] = resultats_t1
resultats_dict['tour2']['voix'] = resultats_t2
for voix_candidat in resultats_bureau_vote.voix:
- resultats_t1[voix_candidat.candidat.id] = voix_candidat.voix_t1
- resultats_t2[voix_candidat.candidat.id] = voix_candidat.voix_t2
+ resultats_t1[voix_candidat.candidat.numero] = voix_candidat.voix_t1
+ resultats_t2[voix_candidat.candidat.numero] = voix_candidat.voix_t2
file = DATA_DIR / "resultats" / "legislatives" / "2022" / "bureau_vote" / f"{bureau_vote.id}.json"
if not file.parent.is_dir():
diff --git a/nupes/scripts/legislatives2022/import_candidats.py b/nupes/scripts/legislatives2022/import_candidats.py
index 443b171..e990702 100644
--- a/nupes/scripts/legislatives2022/import_candidats.py
+++ b/nupes/scripts/legislatives2022/import_candidats.py
@@ -33,7 +33,7 @@ def creer_blocs(engine: Engine, verbose: bool = False) -> None:
def creer_nuances(engine: Engine, verbose: bool = False) -> None:
nuances = [
{"code": "DXG", "nom": "Divers extrême gauche", "couleur": "#BB0000", "bloc_id": 1},
- {"code": "RDG", "nom": "Radical de gauche", "couleur": "#FFD1DC", "bloc_id": 1},
+ {"code": "RDG", "nom": "Parti radical de gauche", "couleur": "#FFD1DC", "bloc_id": 1},
{"code": "NUP", "nom": "Nouvelle union populaire écologique et sociale", "couleur": "#E4032E", "bloc_id": 1},
{"code": "DVG", "nom": "Divers gauche", "couleur": "#FFC0C0", "bloc_id": 1},
{"code": "ECO", "nom": "Écologistes", "couleur": "#77FF77", "bloc_id": 5},