Le problème
Chercher un stage en France, c’est surveiller France Travail, Welcome to the Jungle, HelloWork, La Bonne Alternance, et les alertes LinkedIn et Indeed. La même offre apparaît quatre fois avec des formulations différentes, et chaque candidature demande son propre CV.
JobPilot en fait un pipeline : des sources interchangeables, un seul modèle de données, une file de revue unique, et des documents générés pour chaque offre.
De 8 types de sources à une seule file de revue
- 01Sources
- 02Normalisation
- 03Dédoublonnage
- 04Score
- 05File de revue
- 06CV adapté
Choix techniques
| CHOIX | DOMAINE | POURQUOI |
|---|---|---|
| Ingestion idempotente sur hash du contenu et ID source | Données | Pas de doublons |
| Une seule fonction modifie les statuts, tracée en table d’audit | État | Traçabilité |
| Alertes mail lues en IMAP, en lecture seule | Sécurité | Boîte jamais modifiée |
| Les appels LLM ne journalisent que des métadonnées | Sécurité | Pas de fuite de secrets |
| Doublons détectés sur le texte des missions, pas le discours employeur | Matching | Vrai dédoublonnage |
| Chaque source implémente une même interface, testable | Architecture | Extensible |
Directement depuis le code
Extraits du vrai dépôt et des livrables, raccourcis pour la lecture.
def transition(
db: sqlite3.Connection,
application_id: int,
to_status: str,
*,
detail: dict | None = None,
) -> str:
"""Move an application to to_status if legal; log a status_change event."""
frm = current_status(db, application_id)
allowed = LEGAL.get(frm, set())
if to_status not in allowed:
raise IllegalTransition(
f"application {application_id}: {frm} -> {to_status} not allowed "
f"(legal: {sorted(allowed) or 'none, terminal'})"
)
now = _utc_now()
db.execute(
"UPDATE applications SET status = ?, last_event_at = ? WHERE id = ?",
(to_status, now, application_id),
)
payload = {"from": frm, "to": to_status, **(detail or {})}
log_event(db, application_id, "status_change", payload, commit=False)
db.commit()
return to_statusLa seule fonction autorisée à modifier un statut : les transitions illégales lèvent une exception, et chaque changement est journalisé dans la même transaction.
Résultats
Ce que je ferais autrement
Collecter des offres est facile ; savoir que deux annonces différentes sont le même poste, c’est le vrai problème. Prochaine étape : un tableau de bord web plus propre, et le moteur de matching en bibliothèque autonome.