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 :

Présentation des Fonctionnalités

Le tableau de bord offre les fonctionnalités principales suivantes :

Aperçu du tableau de bord

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.

FRONTEND Vue.js 3 Chart.js Axios BACKEND Node.js / Express PostgreSQL Python (Import des données) Requêtes HTTP Réponses JSON

Fig. 2 - Architecture globale de l'application

Stack Technique

Le projet utilise les technologies suivantes :

Backend

Frontend

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).

who_regions id (PK) region_name countries id (PK) country_name continent population who_region_id (FK) provinces id (PK) province_name country_id (FK) latitude longitude us_counties id (PK) county_name state_id (FK) fips latitude longitude daily_stats id (PK) country_id (FK) date confirmed deaths recovered active new_cases province_stats id (PK) province_id (FK) date confirmed deaths recovered active country_details id (PK) country_id (FK) total_tests tests_per_million cases_per_million deaths_per_million serious_critical county_stats id (PK) county_id (FK) date confirmed deaths

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 :

-- 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 :

GET /api/global-stats
Renvoie les statistiques globales mondiales (total des cas, décès, guérisons, cas actifs)
GET /api/top-countries
Renvoie les 10 pays les plus touchés, classés par nombre de cas confirmés
GET /api/global-timeline
Renvoie l'évolution quotidienne des cas au niveau mondial
GET /api/region-stats
Renvoie les statistiques par région OMS
GET /api/country-timeline/:country
Renvoie l'évolution quotidienne pour un pays spécifique
GET /api/country-comparison?countries=pays1,pays2,...
Renvoie les données pour comparer plusieurs pays
GET /api/filtered-data
Renvoie les données filtrées selon plusieurs critères (région, dates, nombre de cas)
GET /api/countries
Renvoie la liste de tous les pays disponibles
GET /api/country-details/:country
Renvoie les détails spécifiques pour un pays (tests, taux par million, etc.)
// 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.

Fichiers CSV Script Python (import_data.py) Base de données (PostgreSQL) 1. Lecture des fichiers 2. Nettoyage des données 3. Insertion en base

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 :

  1. Lecture des fichiers : Le script lit différents fichiers CSV contenant des données COVID-19
  2. Nettoyage des données : Les données sont nettoyées et normalisées
  3. Insertion des régions WHO : Les régions définies par l'OMS sont insérées en premier
  4. Insertion des pays : Les pays et leurs informations de base sont insérés
  5. Insertion des provinces : Les provinces et états sont insérés avec leurs coordonnées
  6. Insertion des comtés US : Les comtés américains sont insérés pour les données détaillées
  7. Insertion des statistiques : Les données quotidiennes sont insérées pour chaque niveau géographique
Note : Le script utilise des transactions pour assurer l'intégrité des données. Si une erreur se produit pendant l'import, toutes les modifications sont annulées.
# 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>
Note : L'application utilise la Composition API de Vue.js 3 avec la syntaxe <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 statistiques

Affiche les statistiques globales mondiales dans des cartes individuelles.

Événements :

  • toggle-dataset : Émis pour afficher/masquer une série de données sur le graphique
  • show-error : Émis en cas d'erreur lors du chargement des données

CountrySelector.vue

Composant de sélection

Fournit 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 pays
  • country-changed : Émis lors du changement de pays

CountryStats.vue

Composant de statistiques

Affiche les statistiques détaillées pour un pays spécifique.

Props :

  • stats : Objet contenant les statistiques du pays

ErrorMessage.vue

Composant d'interface

Affiche les messages d'erreur à l'utilisateur.

Props :

  • message : Message d'erreur à afficher

LoadingIndicator.vue

Composant d'interface

Affiche un indicateur de chargement pendant les opérations asynchrones.

Props :

  • isLoading : État de chargement (true/false)

WorldChart.vue

Composant de visualisation

Affiche un graphique interactif des données mondiales de COVID-19.

Événements :

  • toggle-loading : Émis pour indiquer l'état de chargement
  • show-error : Émis en cas d'erreur

RegionChart.vue

Composant de visualisation

Affiche un graphique interactif des données par région OMS.

Événements :

  • toggle-loading : Émis pour indiquer l'état de chargement
  • show-error : Émis en cas d'erreur

CountryChart.vue

Composant de visualisation

Affiche 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 chargement
  • show-error : Émis en cas d'erreur
  • update-stats : Émis pour mettre à jour les statistiques du pays
  • country-changed : Émis lorsque l'utilisateur change de pays depuis le classement

ChartOptions.vue

Composant de configuration

Fournit 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 à afficher
  • colors : Couleurs associées à chaque série

Événements :

  • chart-type-change : Émis lors du changement de type de graphique
  • data-format-change : Émis lors du changement de format de données
  • scale-type-change : Émis lors du changement d'échelle
  • toggle-dataset : Émis pour afficher/masquer une série
  • update-color : Émis pour changer la couleur d'une série

ChartControls.vue

Composant de contrôle

Fournit des contrôles pour les fonctionnalités des graphiques (zoom, export, etc.).

Événements :

  • zoom-in : Émis pour zoomer sur le graphique
  • zoom-out : Émis pour dézoomer le graphique
  • reset-zoom : Émis pour réinitialiser le zoom
  • download : Émis pour télécharger le graphique en PNG
  • export : É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 principale

Vue mondiale qui affiche les statistiques globales et l'évolution des cas dans le temps.

Composants utilisés :

  • GlobalStats : Statistiques mondiales
  • WorldChart : Graphique d'évolution mondiale
  • LoadingIndicator : 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 principale

Vue régionale qui affiche les données par région OMS.

Composants utilisés :

  • RegionChart : Graphique par région
  • LoadingIndicator : Indicateur de chargement

PaysView.vue

Vue principale

Vue par pays qui permet de sélectionner un pays et d'afficher ses statistiques détaillées.

Composants utilisés :

  • CountrySelector : Sélecteur de pays
  • CountryStats : Statistiques par pays
  • CountryChart : Graphique par pays
  • LoadingIndicator : 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 futures

Ces 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.

PostgreSQL Express API Axios Vue.js Views Chart Components User Interface Requêtes SQL JSON Data Objects Props Render Interactions utilisateur

Fig. 5 - Flux de données dans l'application

Le flux de données suit les étapes suivantes :

  1. 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
  2. API Backend → Frontend : Les données sont formatées en JSON et envoyées au frontend via les endpoints REST
  3. Axios → Composants Vue : Le client HTTP Axios récupère les données et les transmet aux composants Vue
  4. Composants Vue → Composants Chart : Les données sont passées aux composants de visualisation via des props
  5. Composants Chart → Interface utilisateur : Les graphiques et visualisations sont rendus dans l'interface
  6. 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 :

// 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 :

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

Configuration de la Base de Données

  1. Créer une base de données PostgreSQL nommée covid_db :
  2. psql -U postgres
        CREATE DATABASE covid_db;
        \c covid_db
  3. Créer les tables en exécutant le script SQL fourni :
  4. psql -U postgres -d covid_db -f structure_table.txt

Installation du Backend

  1. Naviguer vers le dossier backend :
  2. cd covid-dashboard/covid-dashboard-vue/backend
  3. Installer les dépendances :
  4. npm install
  5. Créer un fichier .env pour les variables d'environnement :
  6. 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

  1. Installer les dépendances Python :
  2. pip install pandas psycopg2-binary python-dotenv
  3. Exécuter le script d'importation :
  4. python import_data.py
Note : Le script d'importation peut prendre plusieurs minutes en fonction de la quantité de données à traiter.

Installation du Frontend

  1. Naviguer vers le dossier frontend :
  2. cd ../frontend
  3. Installer les dépendances :
  4. npm install

Lancement en Développement

  1. Démarrer le serveur backend :
  2. cd ../backend
        npm start
  3. Dans un autre terminal, démarrer le serveur frontend :
  4. cd ../frontend
        npm run dev
  5. Accéder à l'application via http://localhost:5173

Build pour Production

  1. Créer un build de production du frontend :
  2. cd ../frontend
        npm run build
  3. Copier les fichiers du dossier dist vers le dossier public du backend :
  4. mkdir -p ../backend/public
        cp -r dist/* ../backend/public/
  5. Lancer le serveur en production :
  6. 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 :

  1. Ajouter l'option dans le composant ChartOptions.vue :
  2. <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>
  3. Modifier la fonction createDataset dans les composants de graphique pour gérer le nouveau type :
  4. 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 :

  1. Ajouter la route dans server.js :
  2. // 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 });
            }
        });
  3. Accéder à cet endpoint depuis le frontend avec Axios :
  4. 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 :

  1. Ajouter l'option dans le composant ChartOptions.vue :
  2. <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>
  3. Implémenter le traitement pour ce format dans la fonction processData :
  4. 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 :

  1. Mettre à jour les fichiers CSV sources dans le dossier covid/dataset_clean/
  2. 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 :

  1. Insérez d'abord la région OMS si elle n'existe pas déjà :
  2. INSERT INTO who_regions (region_name) VALUES ('Nouvelle Région');
  3. Puis insérez le pays :
  4. 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 :

  1. Créez une table d'utilisateurs dans la base de données :
  2. 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
        );
  3. Créez les endpoints d'authentification appropriés dans server.js :
  4. // 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 });
            }
        });
  5. Créez les composants frontend nécessaires (formulaires de connexion/inscription)
  6. Implémentez un système de gestion d'état pour l'authentification (avec Vuex ou Pinia)

Comment optimiser les performances pour de grands volumes de données ?

Pour optimiser les performances avec de grands volumes de données :

  1. Indexez les colonnes fréquemment utilisées dans les requêtes :
  2. 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);
  3. Implémentez la pagination côté serveur pour limiter le volume de données renvoyées :
  4. 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 });
            }
        });
  5. Utilisez des vues matérialisées pour les requêtes complexes et fréquentes :
  6. 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;
  7. Implémentez un système de mise en cache côté client et serveur