Tous les guides
IntermédiaireÀ votre rythmeGuide gratuitSection 56/95

Job Tracker — Import CSV et intégrations

Pratiquez les fondations du développement : programmation, web, API, données et usages de l’IA. Des sections et exercices pour construire vos premiers projets.

💡

Contenu gratuit, accessible sans compte. Les outils et modèles évoluent : consultez le dossier actuel pour vérifier leurs versions. Modèles et sources

Section 11.3.17 : Job Tracker — Import CSV et intégrations

🎯 Objectif pédagogique

Ajouter l'import de données dans le Job Tracker : fichiers CSV, export LinkedIn, et intégration API. Vous serez capable de nettoyer et importer des données depuis différentes sources.


Importer ses données existantes

Marc a un fichier CSV avec 50 candidatures passées. Il doit pouvoir les importer dans le Job Tracker sans les re-saisir manuellement. Il veut aussi pouvoir importer ses contacts LinkedIn.

Import CSV avec nettoyage

# importer.py — Import de données depuis différentes sources
import csv
import io
import pandas as pd
import streamlit as st
from database import add_application, get_connection
from datetime import datetime

def parse_csv(file_content, delimiter=','):
    """Parse un fichier CSV et retourne un DataFrame nettoyé."""
    df = pd.read_csv(io.StringIO(file_content), delimiter=delimiter)
    
    # Normaliser les noms de colonnes
    df.columns = [col.strip().lower().replace(' ', '_') for col in df.columns]
    
    return df

def map_columns(df, mapping):
    """Mapper les colonnes du CSV vers les champs du Job Tracker."""
    mapped_data = []
    
    for _, row in df.iterrows():
        entry = \{\}
        for target_field, source_col in mapping.items():
            if source_col and source_col in row.index:
                entry[target_field] = str(row[source_col]).strip()
            else:
                entry[target_field] = None
        
        # Valider les champs obligatoires
        if entry.get('company') and entry.get('position'):
            mapped_data.append(entry)
    
    return mapped_data

def import_to_db(entries):
    """Importer les entrées nettoyées dans la base de données."""
    imported = 0
    skipped = 0
    
    for entry in entries:
        company = entry.pop('company')
        position = entry.pop('position')
        
        # Filtrer les valeurs None ou 'nan'
        clean_kwargs = \{
            k: v for k, v in entry.items() 
            if v and str(v).lower() not in ('none', 'nan', '')
        \}
        
        try:
            add_application(company, position, **clean_kwargs)
            imported += 1
        except Exception as e:
            print(f"Skipped \{company\}/\{position\}: \{e\}")
            skipped += 1
    
    return imported, skipped

def render_import_page():
    """Page d'import dans Streamlit."""
    st.title("⚙️ Import de données")
    
    tab1, tab2, tab3 = st.tabs(["📄 CSV", "💼 LinkedIn", "🔗 API"])
    
    with tab1:
        st.subheader("Import depuis un fichier CSV")
        
        uploaded_file = st.file_uploader(
            "Choisissez votre fichier CSV",
            type=['csv'],
            help="Format attendu : colonnes avec entreprise, poste, statut, date..."
        )
        
        if uploaded_file:
            content = uploaded_file.read().decode('utf-8')
            df = parse_csv(content)
            
            st.write(f"**\{len(df)\} lignes trouvées.** Colonnes détectées :")
            st.write(list(df.columns))
            
            # Prévisualisation
            st.dataframe(df.head(5))
            
            # Mapping des colonnes
            st.subheader("Mapper les colonnes")
            tracker_fields = ['company', 'position', 'status', 'salary_min', 
                            'salary_max', 'location', 'url', 'contact_email', 'notes']
            
            mapping = \{\}
            cols = st.columns(3)
            for i, field in enumerate(tracker_fields):
                with cols[i % 3]:
                    options = ['(ignorer)'] + list(df.columns)
                    selection = st.selectbox(
                        f"→ \{field\}",
                        options,
                        key=f"map_\{field\}"
                    )
                    if selection != '(ignorer)':
                        mapping[field] = selection
            
            if st.button("🚀 Importer"):
                if 'company' not in mapping or 'position' not in mapping:
                    st.error("Les champs 'company' et 'position' sont obligatoires.")
                else:
                    entries = map_columns(df, mapping)
                    imported, skipped = import_to_db(entries)
                    st.success(f"✅ \{imported\} candidatures importées, \{skipped\} ignorées.")
    
    with tab2:
        st.subheader("Import depuis LinkedIn")
        st.markdown("""
        **Comment exporter depuis LinkedIn :**
        1. Allez dans **Paramètres → Données & confidentialité**
        2. Cliquez sur **Obtenir une copie de vos données**
        3. Sélectionnez **Connexions** et/ou **Candidatures**
        4. Vous recevrez un ZIP par email
        5. Uploadez le CSV ici
        """)
        
        linkedin_file = st.file_uploader(
            "Fichier LinkedIn Connections.csv",
            type=['csv'],
            key="linkedin"
        )
        
        if linkedin_file:
            content = linkedin_file.read().decode('utf-8')
            df = parse_csv(content)
            st.dataframe(df.head(5))
            st.info("Mappez les colonnes dans l'onglet CSV ci-dessus.")
    
    with tab3:
        st.subheader("Import via API")
        st.markdown("""
        Vous pouvez aussi importer des données via l'API du Job Tracker.
        """)
        
        st.code("""
# Exemple : import via script Python
import requests

jobs = [
    \{"company": "Google", "position": "Data Analyst", "status": "applied"\},
    \{"company": "Meta", "position": "ML Engineer", "status": "interview"\},
]

for job in jobs:
    response = requests.post(
        "http://localhost:8501/api/import",
        json=job
    )
    print(response.json())
        """, language="python")

Gestion des doublons

# Ajouter dans database.py
def check_duplicate(company, position):
    """Vérifier si une candidature similaire existe déjà."""
    conn = get_connection()
    result = conn.execute(
        """SELECT id FROM applications 
           WHERE LOWER(company) = LOWER(?) AND LOWER(position) = LOWER(?)
           AND archived = 0""",
        (company, position)
    ).fetchone()
    conn.close()
    return result is not None

Encoding et formats CSV

Les CSV français utilisent souvent le point-virgule (;) comme séparateur et l'encodage Windows-1252 ou Latin-1. Si votre import échoue, essayez : pd.read_csv(file, sep=';', encoding='latin-1'). L'export LinkedIn utilise UTF-8 avec virgules.

🏋️ Exercice pratique (20 minutes)

  1. →Créez un fichier CSV test avec 10 candidatures (Company, Position, Status, Date)
  2. →Ajoutez la page Import dans votre Job Tracker
  3. →Importez le CSV et vérifiez dans le Kanban
  4. →Bonus : ajoutez la détection de doublons avant import
Vérification rapide

Pourquoi normaliser les noms de colonnes lors d'un import CSV ?

Vérification rapide

Quel est le principal défi de l'import de données depuis LinkedIn ?

Votre progression

Cette progression est déclarative et conservée dans votre navigateur. L’attestation finale ne constitue pas un diplôme ni une évaluation indépendante.

Voir ma progression et mon attestation

Apprendre l’IA, du premier prompt aux agents

15 guides gratuitsQuiz, schémas et ateliersSans compte

Un cas pratique IA dans votre boîte mail

Méthodes, ressources et nouveaux exercices. Gratuit, avec désinscription à tout moment.