"""Demo script for soutenance. This offline demo loads the saved baseline model and prints a small ranked comparison between two sample candidates against one sample job profile. Run from repo root: /Users/elhadjibassirousy/Desktop/AI-Talent-Finder/.venv/bin/python backend/scripts/demo_soutenance.py """ import sys from pathlib import Path import joblib import numpy as np script_dir = Path(__file__).resolve().parent repo_root = script_dir.parent.parent if str(repo_root / 'backend') not in sys.path: sys.path.insert(0, str(repo_root / 'backend')) from app.services.feature_engineering import PairFeatureMeta, build_pair_features from app.services.normalization import normalize_text def build_features(candidate_text: str, job_text: str, meta: dict): if isinstance(meta, PairFeatureMeta): feature_meta = meta else: tf = meta.get("tfidf") or meta.get("tf") svd = meta.get("svd") feature_meta = PairFeatureMeta(tfidf=tf, svd=svd) return build_pair_features(candidate_text, job_text, feature_meta) def score_candidate(model, meta, candidate_text: str, job_text: str): features = build_features(candidate_text, job_text, meta) try: probability = model.predict_proba(features)[:, 1][0] except Exception: decision = model.decision_function(features)[0] probability = 1 / (1 + np.exp(-decision)) candidate_tokens = set(normalize_text(candidate_text).split()) job_tokens = set(normalize_text(job_text).split()) coverage = len(candidate_tokens & job_tokens) / max(1, len(job_tokens)) blended_score = (0.25 * float(probability) + 0.75 * float(coverage)) * 100 return float(blended_score), float(probability * 100), float(coverage * 100) def main(): model_candidates = [ repo_root / "models" / "final_match_model.joblib", repo_root / "models" / "baseline_model.joblib", ] model_path = next((path for path in model_candidates if path.exists()), None) if model_path is None: raise SystemExit(f"Model not found: {model_candidates[0]}") bundle = joblib.load(model_path) model = bundle["model"] meta = bundle["meta"] job_text = ( "Senior Python Backend Developer\n" "Required: Python, FastAPI, SQL, Docker, AWS, microservices." ) candidates = [ { "name": "Jean Dupont", "text": "Jean Dupont; Senior Python Backend Developer; Python; FastAPI; SQL; Docker; AWS; microservices; backend architecture; 6 years backend experience; team leadership", }, { "name": "Sarah Martin", "text": "Sarah Martin; React; UI/UX; Figma; 4 years frontend experience; communication; design systems; product design", }, ] scored = [] for candidate in candidates: blended_score, model_score, coverage_score = score_candidate(model, meta, candidate["text"], job_text) scored.append((candidate["name"], blended_score, model_score, coverage_score, candidate["text"])) scored.sort(key=lambda item: item[1], reverse=True) print("=== Démo soutenance - Baseline matching ===") print("Poste:") print(job_text) print() for index, (name, score, model_score, coverage_score, text) in enumerate(scored, start=1): print(f"#{index} {name}: {score:.1f}% (modèle {model_score:.1f}%, couverture {coverage_score:.1f}%)") print(f" CV: {text}") print() print("Conclusion: la démo combine le score du modèle et la couverture des compétences pour présenter un ranking lisible.") if __name__ == "__main__": main()