All guides
IntermediateSelf-pacedFree GuideSection 55/95

Job Tracker — Dashboard de statistiques

Practise development foundations: programming, web, APIs, data and AI tools. Work through sections and exercises to build your first projects.

The full lessons for this course are currently available in French. Use the section menu to navigate the French course.

💡

Free content, available without an account. Tools and models change: use the current model guide to check their versions. Models and sources

Section 11.3.16 : Job Tracker — Dashboard de statistiques

🎯 Objectif pédagogique

Construire un dashboard interactif avec des graphiques pour visualiser l'avancement de la recherche d'emploi. Vous serez capable de créer des visualisations avec Plotly dans Streamlit et de transformer des données brutes en insights actionnables.


Les données racontent une histoire

Marc a 30 candidatures dans son Job Tracker. Mais combien ont abouti à un entretien ? Quel est son taux de conversion ? Quelles entreprises répondent le plus vite ? Le dashboard transforme les chiffres bruts en décisions.

Dashboard avec Plotly et Streamlit

# dashboard.py — Statistiques et graphiques
import streamlit as st
import plotly.express as px
import plotly.graph_objects as go
from database import get_connection
from datetime import datetime, timedelta
import pandas as pd

def render_dashboard():
    """Page dashboard avec statistiques et graphiques."""
    st.title("📊 Dashboard — Statistiques de recherche")
    
    # Charger les données
    conn = get_connection()
    df = pd.read_sql_query(
        "SELECT * FROM applications WHERE archived = 0",
        conn
    )
    events_df = pd.read_sql_query("SELECT * FROM events", conn)
    conn.close()
    
    if df.empty:
        st.info("Aucune candidature. Ajoutez-en depuis le Kanban !")
        return
    
    # === KPIs en haut ===
    col1, col2, col3, col4 = st.columns(4)
    
    total = len(df)
    interviews = len(df[df['status'] == 'interview'])
    offers = len(df[df['status'] == 'offer'])
    conversion = (interviews / total * 100) if total > 0 else 0
    
    col1.metric("Total candidatures", total)
    col2.metric("Entretiens", interviews, f"\{conversion:.0f\}%")
    col3.metric("Offres", offers)
    col4.metric("Taux de succès", f"\{(offers/total*100):.0f\}%" if total > 0 else "0%")
    
    st.divider()
    
    # === Graphique 1 : Répartition par statut ===
    col_left, col_right = st.columns(2)
    
    with col_left:
        status_counts = df['status'].value_counts()
        colors = \{
            'wishlist': '#9CA3AF', 'applied': '#3B82F6',
            'screening': '#F59E0B', 'interview': '#8B5CF6',
            'offer': '#10B981', 'rejected': '#EF4444'
        \}
        
        fig_status = px.pie(
            values=status_counts.values,
            names=status_counts.index,
            title="Répartition par statut",
            color=status_counts.index,
            color_discrete_map=colors
        )
        st.plotly_chart(fig_status, use_container_width=True)
    
    with col_right:
        # === Graphique 2 : Candidatures par semaine ===
        df['created_date'] = pd.to_datetime(df['created_at']).dt.date
        weekly = df.groupby(
            pd.to_datetime(df['created_at']).dt.isocalendar().week
        ).size().reset_index(name='count')
        weekly.columns = ['week', 'count']
        
        fig_weekly = px.bar(
            weekly, x='week', y='count',
            title="Candidatures par semaine",
            labels=\{'week': 'Semaine', 'count': 'Nombre'\}
        )
        fig_weekly.update_traces(marker_color='#0891B2')
        st.plotly_chart(fig_weekly, use_container_width=True)
    
    st.divider()
    
    # === Graphique 3 : Funnel de conversion ===
    funnel_stages = ['applied', 'screening', 'interview', 'offer']
    funnel_values = []
    for stage in funnel_stages:
        # Compter ceux qui ont atteint au moins ce stade
        stage_idx = funnel_stages.index(stage)
        count = len(df[df['status'].isin(funnel_stages[stage_idx:])])
        funnel_values.append(count)
    
    fig_funnel = go.Figure(go.Funnel(
        y=['Applied', 'Screening', 'Interview', 'Offer'],
        x=funnel_values,
        textinfo="value+percent initial",
        marker=dict(color=['#3B82F6', '#F59E0B', '#8B5CF6', '#10B981'])
    ))
    fig_funnel.update_layout(title="Funnel de conversion")
    st.plotly_chart(fig_funnel, use_container_width=True)
    
    # === Graphique 4 : Temps moyen par étape ===
    st.subheader("⏱️ Temps moyen par étape")
    
    stage_durations = []
    for _, row in df.iterrows():
        app_events = events_df[events_df['application_id'] == row['id']].sort_values('event_date')
        if len(app_events) >= 2:
            first = pd.to_datetime(app_events.iloc[0]['event_date'])
            last = pd.to_datetime(app_events.iloc[-1]['event_date'])
            days = (last - first).days
            stage_durations.append(\{
                'company': row['company'],
                'status': row['status'],
                'days': days
            \})
    
    if stage_durations:
        dur_df = pd.DataFrame(stage_durations)
        avg_by_status = dur_df.groupby('status')['days'].mean().round(1)
        
        fig_duration = px.bar(
            x=avg_by_status.index,
            y=avg_by_status.values,
            title="Temps moyen par statut (jours)",
            labels=\{'x': 'Statut', 'y': 'Jours'\}
        )
        st.plotly_chart(fig_duration, use_container_width=True)
    
    # === Tableau détaillé ===
    with st.expander("📋 Toutes les candidatures"):
        display_df = df[['company', 'position', 'status', 'location', 'salary_min', 'created_at']].copy()
        display_df.columns = ['Entreprise', 'Poste', 'Statut', 'Lieu', 'Salaire min', 'Date']
        st.dataframe(display_df, use_container_width=True)

Intégration dans l'app principale

# Dans app.py — Ajouter la page Dashboard
from dashboard import render_dashboard

# Dans le routeur de pages :
if page == "📊 Dashboard":
    render_dashboard()

Plotly vs Matplotlib dans Streamlit

Plotly est préféré dans Streamlit car les graphiques sont interactifs (zoom, hover, export). Matplotlib produit des images statiques. Pour un dashboard, l'interactivité fait une vraie différence — Marc peut survoler un point pour voir les détails.

🏋️ Exercice pratique (25 minutes)

  1. →Ajoutez dashboard.py à votre Job Tracker
  2. →Installez Plotly : pip install plotly
  3. →Ajoutez au moins 10 candidatures avec différents statuts
  4. →Naviguez vers le Dashboard et analysez vos métriques
  5. →Bonus : ajoutez un graphique de salaire moyen par statut
Quick Check

Quel graphique est le plus utile pour voir le taux de conversion des candidatures ?

Quick Check

Pourquoi utiliser Plotly plutôt que Matplotlib dans un dashboard Streamlit ?

Your progress

Progress is self-reported and stored in your browser. The final completion record is not a qualification or an independent assessment.

View progress and completion record

Learn AI, from prompts to agents

15 free guidesQuizzes, diagrams and workshopsNo account needed

One practical AI workflow in your inbox

Methods, resources and new exercises. Free, unsubscribe at any time.