Introduction au Tableau de Bord COVID-19
Bienvenue dans la documentation technique du tableau de bord COVID-19. Ce projet fournit une interface interactive pour visualiser et analyser les données de la pandémie de COVID-19 à différentes échelles : mondiale, régionale et par pays.
Objectifs du Projet
Ce tableau de bord a été conçu pour répondre aux besoins suivants :
- Visualiser l'évolution de la pandémie à l'échelle mondiale
- Analyser les tendances par région géographique
- Comparer les statistiques entre différents pays
- Fournir des données détaillées pour chaque pays
- Permettre l'exploration interactive des données
Présentation des Fonctionnalités
Le tableau de bord offre les fonctionnalités principales suivantes :
- Vue Mondiale : statistiques globales et évolution dans le temps
- Vue par Région : analyse par région OMS (Organisation Mondiale de la Santé)
- Vue par Pays : statistiques détaillées et évolution par pays
- Visualisations interactives : graphiques configurables (type, échelle, format de données)
- Export de données : téléchargement des graphiques et export CSV des données
Fig. 1 - Aperçu de l'interface du tableau de bord COVID-19
Architecture Globale
Le tableau de bord COVID-19 est construit selon une architecture client-serveur moderne, avec une séparation claire entre le frontend et le backend. Cette approche permet une maintenance plus facile et une meilleure évolutivité du projet.
Fig. 2 - Architecture globale de l'application
Stack Technique
Le projet utilise les technologies suivantes :
Backend
- Node.js : Environnement d'exécution JavaScript côté serveur
- Express : Framework web pour Node.js
- PostgreSQL : Système de gestion de base de données relationnelle
- Python : Utilisé pour les scripts d'importation de données
- JWT : JSON Web Tokens pour l'authentification
Frontend
- Vue.js 3 : Framework JavaScript progressif
- Vite : Outil de build moderne pour les applications web
- Chart.js : Bibliothèque de visualisation de données
- Axios : Client HTTP basé sur les promesses
- FontAwesome : Bibliothèque d'icônes
Structure du Projet
Le projet est organisé selon la structure suivante :
covid-dashboard-vue/
├── backend/ # Serveur Node.js et API REST
│ ├── import_data.py # Script Python pour l'import des données
│ ├── server.js # Point d'entrée du serveur
│ ├── package.json # Dépendances du backend
│ └── ...
└── frontend/ # Application Vue.js 3
├── src/ # Code source Vue
│ ├── components/ # Composants Vue
│ │ ├── Charts/ # Composants de visualisation
│ │ ├── Dashboard/ # Composants du tableau de bord
│ │ └── NavBar/ # Barre de navigation
│ ├── assets/ # Ressources statiques
│ ├── App.vue # Composant racine
│ └── main.js # Point d'entrée de l'application
├── package.json # Dépendances du frontend
└── vite.config.js # Configuration de Vite
Backend
Le backend de l'application est basé sur Node.js avec Express pour l'API REST et PostgreSQL pour le stockage des données. Cette section détaille l'architecture de la base de données, les endpoints de l'API et le processus d'importation des données.
Schéma de Base de Données
La base de données covid_db utilise un modèle relationnel pour stocker les données COVID-19
à différents niveaux géographiques (mondial, régional, national, provincial).
Fig. 3 - Schéma relationnel de la base de données covid_db
La base de données est conçue avec les tables suivantes :
- who_regions : Stocke les régions définies par l'OMS
- countries : Contient les informations de base sur les pays (nom, continent, population)
- provinces : Enregistre les provinces ou états des pays, avec leurs coordonnées géographiques
- us_counties : Contient les comtés des États-Unis (spécifique aux données américaines détaillées)
- daily_stats : Statistiques quotidiennes par pays (cas confirmés, décès, guérisons, cas actifs)
- province_stats : Statistiques quotidiennes par province
- county_stats : Statistiques quotidiennes par comté (États-Unis)
- country_details : Informations détaillées supplémentaires par pays (tests, taux par million, etc.)
-- Structure complète de la base de données
CREATE DATABASE covid_db;
-- Table des régions WHO
CREATE TABLE who_regions (
id SERIAL PRIMARY KEY,
region_name VARCHAR(50) UNIQUE
);
-- Table des pays
CREATE TABLE countries (
id SERIAL PRIMARY KEY,
country_name VARCHAR(100) UNIQUE,
continent VARCHAR(50),
population BIGINT,
who_region_id INTEGER REFERENCES who_regions(id)
);
-- Table des provinces/états
CREATE TABLE provinces (
id SERIAL PRIMARY KEY,
province_name VARCHAR(100),
country_id INTEGER REFERENCES countries(id),
latitude FLOAT,
longitude FLOAT,
UNIQUE(province_name, country_id)
);
-- Table des comtés US
CREATE TABLE us_counties (
id SERIAL PRIMARY KEY,
county_name VARCHAR(100),
state_id INTEGER REFERENCES provinces(id),
fips VARCHAR(10),
latitude FLOAT,
longitude FLOAT,
UNIQUE(fips)
);
-- Table des statistiques quotidiennes par pays
CREATE TABLE daily_stats (
id SERIAL PRIMARY KEY,
country_id INTEGER REFERENCES countries(id),
date DATE,
confirmed INTEGER,
deaths INTEGER,
recovered INTEGER,
active INTEGER,
new_cases INTEGER,
new_deaths INTEGER,
new_recovered INTEGER,
UNIQUE(country_id, date)
);
-- Table des statistiques quotidiennes par province
CREATE TABLE province_stats (
id SERIAL PRIMARY KEY,
province_id INTEGER REFERENCES provinces(id),
date DATE,
confirmed INTEGER,
deaths INTEGER,
recovered INTEGER,
active INTEGER,
UNIQUE(province_id, date)
);
-- Table des statistiques quotidiennes par comté US
CREATE TABLE county_stats (
id SERIAL PRIMARY KEY,
county_id INTEGER REFERENCES us_counties(id),
date DATE,
confirmed INTEGER,
deaths INTEGER,
UNIQUE(county_id, date)
);
-- Table des statistiques détaillées par pays
CREATE TABLE country_details (
id SERIAL PRIMARY KEY,
country_id INTEGER REFERENCES countries(id),
total_tests BIGINT,
tests_per_million FLOAT,
cases_per_million FLOAT,
deaths_per_million FLOAT,
serious_critical INTEGER,
last_updated DATE,
UNIQUE(country_id, last_updated)
);
API Endpoints
Le backend expose plusieurs endpoints REST pour permettre au frontend d'accéder aux données. Voici les principaux endpoints :
// Exemple d'implémentation de l'API global-stats dans server.js
app.get('/api/global-stats', async (req, res) => {
try {
const result = await pool.query(`
SELECT
SUM(confirmed) as total_confirmed,
SUM(deaths) as total_deaths,
SUM(recovered) as total_recovered,
SUM(active) as total_active
FROM daily_stats
WHERE date = (SELECT MAX(date) FROM daily_stats)
`);
res.json(result.rows[0]);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
Import des Données
Le processus d'import des données est géré par un script Python import_data.py. Ce script se
connecte à la base de données PostgreSQL, lit plusieurs fichiers CSV contenant les données COVID-19, et
les insère dans les tables correspondantes.
Fig. 4 - Processus d'importation des données
Le script utilise la bibliothèque Python pandas pour la manipulation des données et
psycopg2 pour se connecter à PostgreSQL. L'import des données se déroule en plusieurs
étapes :
- Lecture des fichiers : Le script lit différents fichiers CSV contenant des données COVID-19
- Nettoyage des données : Les données sont nettoyées et normalisées
- Insertion des régions WHO : Les régions définies par l'OMS sont insérées en premier
- Insertion des pays : Les pays et leurs informations de base sont insérés
- Insertion des provinces : Les provinces et états sont insérés avec leurs coordonnées
- Insertion des comtés US : Les comtés américains sont insérés pour les données détaillées
- Insertion des statistiques : Les données quotidiennes sont insérées pour chaque niveau géographique
# Extrait du script import_data.py
def insert_who_regions(conn, df):
"""Insertion des régions WHO"""
with conn.cursor() as cur:
for region in pd.unique(df['WHO Region']):
cur.execute("""
INSERT INTO who_regions (region_name)
VALUES (%s)
ON CONFLICT (region_name) DO NOTHING
RETURNING id
""", (region,))
conn.commit()
def insert_countries(conn, df_countries, df_worldometer):
"""Insertion des pays"""
with conn.cursor() as cur:
# Récupération du mapping des régions WHO
cur.execute("SELECT region_name, id FROM who_regions")
who_regions_map = dict(cur.fetchall())
# Fusion et insertion des données pays
for _, row in df_countries.iterrows():
worldometer_data = df_worldometer[
df_worldometer['Country/Region'] == row['Country/Region']
].iloc[0] if len(df_worldometer[df_worldometer['Country/Region'] == row['Country/Region']]) > 0 else None
cur.execute("""
INSERT INTO countries (country_name, continent, population, who_region_id)
VALUES (%s, %s, %s, %s)
ON CONFLICT (country_name) DO UPDATE
SET continent = EXCLUDED.continent,
population = EXCLUDED.population,
who_region_id = EXCLUDED.who_region_id
RETURNING id
""", (
row['Country/Region'],
worldometer_data['Continent'] if worldometer_data is not None else None,
worldometer_data['Population'] if worldometer_data is not None else None,
who_regions_map.get(row['WHO Region'])
))
conn.commit()
Frontend
Le frontend de l'application est développé avec Vue.js 3, un framework JavaScript progressif. Cette section détaille l'architecture frontend, les composants principaux et les vues de l'application.
Structure Vue.js
L'application Vue.js est organisée selon une architecture modulaire et composable. Le point d'entrée est
le fichier main.js qui monte l'application sur l'élément DOM avec l'ID "app".
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import '@fortawesome/fontawesome-free/css/all.css'
createApp(App).mount('#app')
Le composant racine App.vue contient la structure principale de l'application et gère la
navigation entre les différentes vues :
<template>
<div>
<nav-bar :active-view="activeView" @change-view="changeView" />
<div class="container">
<error-message :message="errorMessage" />
<mondial-view v-if="activeView === 'mondial'" @show-error="showError" />
<regions-view v-if="activeView === 'regions'" @show-error="showError" />
<pays-view v-if="activeView === 'pays'" @show-error="showError" />
<correlation-view v-if="activeView === 'correlation'" @show-error="showError" />
<tendances-view v-if="activeView === 'tendances'" @show-error="showError" />
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
import NavBar from './components/NavBar/NavBar.vue';
import ErrorMessage from './components/Dashboard/ErrorMessage.vue';
import MondialView from './components/Dashboard/MondialView.vue';
import RegionsView from './components/Dashboard/RegionsView.vue';
import PaysView from './components/Dashboard/PaysView.vue';
import CorrelationView from './components/Dashboard/CorrelationView.vue';
import TendancesView from './components/Dashboard/TendancesView.vue';
const activeView = ref('mondial');
const errorMessage = ref('');
function changeView(view) {
activeView.value = view;
errorMessage.value = '';
}
function showError(message) {
errorMessage.value = message;
setTimeout(() => {
if (errorMessage.value === message) {
errorMessage.value = '';
}
}, 5000);
}
</script>
<script setup> qui offre une meilleure organisation du code et une meilleure
réutilisabilité.
Composants
L'application est construite avec plusieurs composants réutilisables, organisés en catégories :
GlobalStats.vue
Composant de statistiquesAffiche les statistiques globales mondiales dans des cartes individuelles.
Événements :
toggle-dataset: Émis pour afficher/masquer une série de données sur le graphiqueshow-error: Émis en cas d'erreur lors du chargement des données
CountrySelector.vue
Composant de sélectionFournit un sélecteur de pays avec fonctionnalité de recherche.
Props :
value: Pays actuellement sélectionné
Événements :
input: Émis lors de la sélection d'un payscountry-changed: Émis lors du changement de pays
CountryStats.vue
Composant de statistiquesAffiche les statistiques détaillées pour un pays spécifique.
Props :
stats: Objet contenant les statistiques du pays
ErrorMessage.vue
Composant d'interfaceAffiche les messages d'erreur à l'utilisateur.
Props :
message: Message d'erreur à afficher
LoadingIndicator.vue
Composant d'interfaceAffiche un indicateur de chargement pendant les opérations asynchrones.
Props :
isLoading: État de chargement (true/false)
WorldChart.vue
Composant de visualisationAffiche un graphique interactif des données mondiales de COVID-19.
Événements :
toggle-loading: Émis pour indiquer l'état de chargementshow-error: Émis en cas d'erreur
RegionChart.vue
Composant de visualisationAffiche un graphique interactif des données par région OMS.
Événements :
toggle-loading: Émis pour indiquer l'état de chargementshow-error: Émis en cas d'erreur
CountryChart.vue
Composant de visualisationAffiche un graphique interactif des données pour un pays spécifique.
Props :
selectedCountry: Pays sélectionné pour l'affichage
Événements :
toggle-loading: Émis pour indiquer l'état de chargementshow-error: Émis en cas d'erreurupdate-stats: Émis pour mettre à jour les statistiques du payscountry-changed: Émis lorsque l'utilisateur change de pays depuis le classement
ChartOptions.vue
Composant de configurationFournit des options de configuration pour les graphiques (type, échelle, format de données).
Props :
chartType: Type de graphique (ligne, barre, etc.)dataFormat: Format des données (brut, quotidien, etc.)scaleType: Type d'échelle (linéaire, logarithmique)datasets: Séries de données à affichercolors: Couleurs associées à chaque série
Événements :
chart-type-change: Émis lors du changement de type de graphiquedata-format-change: Émis lors du changement de format de donnéesscale-type-change: Émis lors du changement d'échelletoggle-dataset: Émis pour afficher/masquer une sérieupdate-color: Émis pour changer la couleur d'une série
ChartControls.vue
Composant de contrôleFournit des contrôles pour les fonctionnalités des graphiques (zoom, export, etc.).
Événements :
zoom-in: Émis pour zoomer sur le graphiquezoom-out: Émis pour dézoomer le graphiquereset-zoom: Émis pour réinitialiser le zoomdownload: Émis pour télécharger le graphique en PNGexport: Émis pour exporter les données en CSV
Vues
L'application est divisée en plusieurs vues principales, chacune représentant une section fonctionnelle du tableau de bord :
MondialView.vue
Vue principaleVue mondiale qui affiche les statistiques globales et l'évolution des cas dans le temps.
Composants utilisés :
GlobalStats: Statistiques mondialesWorldChart: Graphique d'évolution mondialeLoadingIndicator: Indicateur de chargement
<template>
<div id="mondial" class="visualization-section">
<global-stats @toggle-dataset="toggleDataset" @show-error="showError" />
<world-chart ref="worldChart" @toggle-loading="setLoading" @show-error="showError" />
<loading-indicator :is-loading="isLoading" />
</div>
</template>
<script setup>
import { ref } from 'vue';
import GlobalStats from './GlobalStats.vue';
import WorldChart from '../Charts/WorldChart.vue';
import LoadingIndicator from './LoadingIndicator.vue';
const isLoading = ref(false);
const worldChart = ref(null);
function toggleDataset(datasetName) {
if (worldChart.value) {
worldChart.value.toggleDataset(datasetName);
}
}
function showError(message) {
emit('show-error', message);
}
function setLoading(loading) {
isLoading.value = loading;
}
</script>
RegionsView.vue
Vue principaleVue régionale qui affiche les données par région OMS.
Composants utilisés :
RegionChart: Graphique par régionLoadingIndicator: Indicateur de chargement
PaysView.vue
Vue principaleVue par pays qui permet de sélectionner un pays et d'afficher ses statistiques détaillées.
Composants utilisés :
CountrySelector: Sélecteur de paysCountryStats: Statistiques par paysCountryChart: Graphique par paysLoadingIndicator: Indicateur de chargement
<template>
<div id="pays" class="visualization-section">
<div class="options-panel">
<div class="options-grid">
<div class="option-group">
<label>Sélectionner un pays</label>
<country-selector :value="selectedCountry" @country-changed="updateSelectedCountry" @show-error="showError" />
</div>
</div>
</div>
<country-stats :stats="countryStats" />
<country-chart :selectedCountry="selectedCountry" @toggle-loading="setLoading" @show-error="showError" @update-stats="updateCountryStats" @country-changed="updateSelectedCountry" />
<loading-indicator :is-loading="isLoading" />
</div>
</template>
<script setup>
import { ref } from 'vue';
import CountrySelector from './CountrySelector.vue';
import CountryStats from './CountryStats.vue';
import CountryChart from '../Charts/CountryChart.vue';
import LoadingIndicator from './LoadingIndicator.vue';
const selectedCountry = ref('France');
const countryStats = ref({
confirmed: null,
deaths: null,
recovered: null,
active: null,
mortalityRate: null
});
const isLoading = ref(false);
function updateSelectedCountry(country) {
selectedCountry.value = country;
}
function updateCountryStats(stats) {
countryStats.value = stats;
}
function showError(message) {
emit('show-error', message);
}
function setLoading(loading) {
isLoading.value = loading;
}
</script>
CorrelationView.vue et TendancesView.vue
Vues futuresCes vues sont prévues pour des fonctionnalités futures du tableau de bord :
- CorrelationView : Analyse des corrélations entre différentes métriques (à venir)
- TendancesView : Analyse prédictive des tendances futures (à venir)
<template>
<div id="correlation" class="visualization-section">
<div class="coming-soon">
<h2>Analyse des corrélations</h2>
<p>Cette fonctionnalité sera disponible prochainement.</p>
</div>
</div>
</template>
Flux de Données
Cette section explique comment les données circulent dans l'application, depuis la base de données jusqu'à l'affichage dans l'interface utilisateur.
Fig. 5 - Flux de données dans l'application
Le flux de données suit les étapes suivantes :
- Base de données → API Backend : Les données sont extraites de PostgreSQL par les requêtes SQL exécutées par le serveur Express
- API Backend → Frontend : Les données sont formatées en JSON et envoyées au frontend via les endpoints REST
- Axios → Composants Vue : Le client HTTP Axios récupère les données et les transmet aux composants Vue
- Composants Vue → Composants Chart : Les données sont passées aux composants de visualisation via des props
- Composants Chart → Interface utilisateur : Les graphiques et visualisations sont rendus dans l'interface
- Interface utilisateur → API Backend : Les interactions de l'utilisateur déclenchent de nouvelles requêtes à l'API
Gestion de l'État
L'application utilise la Composition API de Vue.js 3 pour gérer l'état local dans chaque composant :
- Chaque vue principale gère son propre état local (ex: pays sélectionné, options de graphique)
- Les événements sont utilisés pour communiquer entre composants parents et enfants
- Les références
refsont utilisées pour stocker les données réactives
// Exemple de gestion d'état dans PaysView.vue
const selectedCountry = ref('France'); // État local pour le pays sélectionné
const countryStats = ref({ // État local pour les statistiques du pays
confirmed: null,
deaths: null,
recovered: null,
active: null,
mortalityRate: null
});
const isLoading = ref(false); // État local pour l'indicateur de chargement
// Fonction pour mettre à jour l'état
function updateSelectedCountry(country) {
selectedCountry.value = country;
}
Optimisations de Performance
Pour garantir des performances optimales, l'application implémente plusieurs optimisations :
- Chargement paresseux des vues : Seule la vue active est rendue (
v-if) - Requêtes optimisées : Les requêtes SQL sont optimisées pour récupérer uniquement les données nécessaires
- Mise en cache côté client : Axios peut mettre en cache certaines réponses pour éviter des requêtes répétées
- Optimisation des rendus : Chart.js est configuré pour optimiser les rendus des graphiques
Installation et Déploiement
Cette section fournit des instructions détaillées pour installer, configurer et déployer l'application dashboard COVID-19.
Prérequis
- Node.js (v14+)
- PostgreSQL (v12+)
- Python 3.7+ (pour l'import des données)
- npm ou yarn
Configuration de la Base de Données
- Créer une base de données PostgreSQL nommée
covid_db: - Créer les tables en exécutant le script SQL fourni :
psql -U postgres
CREATE DATABASE covid_db;
\c covid_db
psql -U postgres -d covid_db -f structure_table.txt
Installation du Backend
- Naviguer vers le dossier backend :
- Installer les dépendances :
- Créer un fichier
.envpour les variables d'environnement :
cd covid-dashboard/covid-dashboard-vue/backend
npm install
DB_NAME=covid_db
DB_USER=postgres
DB_PASSWORD=votre_mot_de_passe
DB_HOST=localhost
DB_PORT=5432
JWT_SECRET=votre_secret_jwt
ADMIN_USERNAME=admin
ADMIN_PASSWORD=mot_de_passe_admin
PORT=3000
Import des Données
- Installer les dépendances Python :
- Exécuter le script d'importation :
pip install pandas psycopg2-binary python-dotenv
python import_data.py
Installation du Frontend
- Naviguer vers le dossier frontend :
- Installer les dépendances :
cd ../frontend
npm install
Lancement en Développement
- Démarrer le serveur backend :
- Dans un autre terminal, démarrer le serveur frontend :
- Accéder à l'application via http://localhost:5173
cd ../backend
npm start
cd ../frontend
npm run dev
Build pour Production
- Créer un build de production du frontend :
- Copier les fichiers du dossier
distvers le dossierpublicdu backend : - Lancer le serveur en production :
cd ../frontend
npm run build
mkdir -p ../backend/public
cp -r dist/* ../backend/public/
cd ../backend
NODE_ENV=production npm start
Déploiement avec Docker (Optionnel)
L'application peut également être déployée à l'aide de Docker :
# Exemple de Dockerfile pour le backend
FROM node:14-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
Tutoriels
Ajout d'un Nouveau Type de Graphique
Ce tutoriel explique comment ajouter un nouveau type de graphique à l'application :
- Ajouter l'option dans le composant
ChartOptions.vue: - Modifier la fonction
createDatasetdans les composants de graphique pour gérer le nouveau type :
<select :id="chartTypeId" v-model="localChartType" @change="emitChartTypeChange">
<option value="line">Ligne</option>
<option value="bar">Barres</option>
<option value="area">Aire</option>
<option value="scatter">Nuage de points</option> <!-- Nouveau type -->
<option v-if="showPieOptions" value="pie">Camembert</option>
<option v-if="showPieOptions" value="doughnut">Anneau</option>
</select>
function createDataset(label, data, color) {
return {
label: label,
data: data,
borderColor: color,
backgroundColor: chartType.value === 'bar' ? `${color}88` : `${color}22`,
fill: chartType.value === 'area',
tension: 0.4,
id: label.toLowerCase().replace(/\s+/g, ''),
// Configurations spécifiques au type scatter
pointRadius: chartType.value === 'scatter' ? 4 : 0,
pointHoverRadius: 6
};
}
Ajout d'un Nouvel Endpoint API
Ce tutoriel explique comment ajouter un nouvel endpoint à l'API backend :
- Ajouter la route dans
server.js: - Accéder à cet endpoint depuis le frontend avec Axios :
// API pour obtenir les prédictions de cas
app.get('/api/predictions/:country', async (req, res) => {
try {
const result = await pool.query(`
WITH last_30_days AS (
SELECT
d.date,
d.confirmed
FROM daily_stats d
JOIN countries c ON d.country_id = c.id
WHERE c.country_name = $1
ORDER BY d.date DESC
LIMIT 30
)
SELECT
date,
confirmed,
-- Calcul simple de prédiction basé sur la tendance des 30 derniers jours
confirmed + (
(SELECT MAX(confirmed) - MIN(confirmed) FROM last_30_days) / 30
) * 7 AS prediction_7_days
FROM last_30_days
ORDER BY date DESC
LIMIT 1
`, [req.params.country]);
res.json(result.rows[0]);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
async function loadPredictions(country) {
try {
const response = await axios.get(`/api/predictions/${country}`);
return response.data;
} catch (error) {
console.error('Erreur lors du chargement des prédictions:',error);
throw error;
}
}
Modification du Format des Données
Ce tutoriel explique comment ajouter un nouveau format de données pour les graphiques :
- Ajouter l'option dans le composant
ChartOptions.vue: - Implémenter le traitement pour ce format dans la fonction
processData:
<select :id="dataFormatId" v-model="localDataFormat" @change="emitDataFormatChange">
<option value="raw">Valeurs brutes</option>
<option value="daily">Variation quotidienne</option>
<option value="weekly">Moyenne mobile (7j)</option>
<option value="monthly">Moyenne mobile (30j)</option>
<option value="percentage">Pourcentage du total</option> <!-- Nouveau format -->
</select>
const processData = (data, format) => {
switch (format) {
case 'percentage':
// Calcul du pourcentage par rapport au total
const totals = data.map(item => item.confirmed + item.deaths + item.recovered + item.active);
return data.map((item, i) => ({
...item,
confirmed: (item.confirmed / totals[i]) * 100,
deaths: (item.deaths / totals[i]) * 100,
recovered: (item.recovered / totals[i]) * 100,
active: (item.active / totals[i]) * 100
}));
case 'daily':
return data.map((item, index) => {
if (index === 0) return item;
return {
...item,
confirmed: item.confirmed - data[index - 1].confirmed,
deaths: item.deaths - data[index - 1].deaths,
recovered: item.recovered - data[index - 1].recovered,
active: item.active - data[index - 1].active
};
});
case 'weekly':
return calculateMovingAverage(data, 7);
case 'monthly':
return calculateMovingAverage(data, 30);
default:
return data;
}
};
FAQs - Questions Fréquemment Posées
Comment mettre à jour les données COVID-19 ?
Pour mettre à jour les données, vous devez :
- Mettre à jour les fichiers CSV sources dans le dossier
covid/dataset_clean/ - Exécuter le script d'importation :
python import_data.py
Le script détectera automatiquement les nouvelles données et mettra à jour la base de données en conséquence.
Comment ajouter un nouveau pays dans l'application ?
Les pays sont automatiquement extraits des fichiers CSV lors de l'importation. Si vous souhaitez ajouter un nouveau pays manuellement :
- Insérez d'abord la région OMS si elle n'existe pas déjà :
- Puis insérez le pays :
INSERT INTO who_regions (region_name) VALUES ('Nouvelle Région');
INSERT INTO countries (country_name, continent, population, who_region_id)
VALUES ('Nouveau Pays', 'Continent', 1000000,
(SELECT id FROM who_regions WHERE region_name = 'Nouvelle Région'));
Comment modifier les couleurs par défaut des graphiques ?
Les couleurs par défaut sont définies dans les composants de graphique. Pour les modifier
globalement, recherchez les objets chartConfig dans chaque composant de graphique :
const chartConfig = ref({
datasets: {
confirmed: true,
deaths: true,
recovered: true,
active: true
},
colors: {
confirmed: '#1a73e8', // Bleu
deaths: '#dc3545', // Rouge
recovered: '#28a745', // Vert
active: '#ffc107' // Jaune
}
});
Modifiez les codes de couleur hexadécimaux selon vos préférences.
L'application peut-elle être utilisée hors ligne ?
Non, l'application nécessite une connexion à la base de données PostgreSQL pour fonctionner. Cependant, vous pouvez configurer l'ensemble du système sur un réseau local si nécessaire.
Pour une version hors ligne, vous pourriez modifier l'application pour qu'elle utilise des fichiers JSON statiques au lieu d'une base de données, mais cela nécessiterait des modifications importantes du code.
Comment ajouter un système d'authentification complet ?
Le backend inclut déjà un système d'authentification de base avec JWT. Pour implémenter un système complet :
- Créez une table d'utilisateurs dans la base de données :
- Créez les endpoints d'authentification appropriés dans
server.js: - Créez les composants frontend nécessaires (formulaires de connexion/inscription)
- Implémentez un système de gestion d'état pour l'authentification (avec Vuex ou Pinia)
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
role VARCHAR(20) NOT NULL DEFAULT 'user',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
// Inscription
app.post('/api/auth/register', async (req, res) => {
try {
const { username, password, email } = req.body;
// Hashage du mot de passe
const hashedPassword = await bcrypt.hash(password, 10);
// Insertion dans la base de données
const result = await pool.query(`
INSERT INTO users (username, password, email)
VALUES ($1, $2, $3)
RETURNING id, username, email, role
`, [username, hashedPassword, email]);
res.status(201).json(result.rows[0]);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Connexion
app.post('/api/auth/login', async (req, res) => {
try {
const { username, password } = req.body;
// Vérification de l'utilisateur
const result = await pool.query(`
SELECT * FROM users WHERE username = $1
`, [username]);
if (result.rows.length === 0) {
return res.status(401).json({ error: 'Identifiants incorrects' });
}
const user = result.rows[0];
// Vérification du mot de passe
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
return res.status(401).json({ error: 'Identifiants incorrects' });
}
// Génération du token JWT
const token = jwt.sign(
{ id: user.id, username: user.username, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
res.json({ token });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
Comment optimiser les performances pour de grands volumes de données ?
Pour optimiser les performances avec de grands volumes de données :
- Indexez les colonnes fréquemment utilisées dans les requêtes :
- Implémentez la pagination côté serveur pour limiter le volume de données renvoyées :
- Utilisez des vues matérialisées pour les requêtes complexes et fréquentes :
- Implémentez un système de mise en cache côté client et serveur
CREATE INDEX idx_daily_stats_date ON daily_stats(date);
CREATE INDEX idx_daily_stats_country_id ON daily_stats(country_id);
CREATE INDEX idx_daily_stats_country_date ON daily_stats(country_id, date);
app.get('/api/global-timeline', async (req, res) => {
try {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 30;
const offset = (page - 1) * limit;
const result = await pool.query(`
SELECT
date,
SUM(confirmed) as confirmed,
SUM(deaths) as deaths,
SUM(recovered) as recovered,
SUM(active) as active
FROM daily_stats
GROUP BY date
ORDER BY date
LIMIT $1 OFFSET $2
`, [limit, offset]);
// Récupération du nombre total d'enregistrements pour la pagination
const countResult = await pool.query(`
SELECT COUNT(DISTINCT date) as total FROM daily_stats
`);
const totalPages = Math.ceil(countResult.rows[0].total / limit);
res.json({
data: result.rows,
pagination: {
page,
limit,
totalRecords: countResult.rows[0].total,
totalPages
}
});
} catch (err) {
res.status(500).json({ error: err.message });
}
});
CREATE MATERIALIZED VIEW mv_global_daily_stats AS
SELECT
date,
SUM(confirmed) as confirmed,
SUM(deaths) as deaths,
SUM(recovered) as recovered,
SUM(active) as active
FROM daily_stats
GROUP BY date
ORDER BY date;
-- Création d'un index sur la vue matérialisée
CREATE INDEX idx_mv_global_daily_stats_date ON mv_global_daily_stats(date);
-- Rafraîchissement périodique de la vue matérialisée
REFRESH MATERIALIZED VIEW mv_global_daily_stats;