| from flask import Flask, render_template, jsonify, request
|
| import pandas as pd
|
| import numpy as np
|
| import pickle
|
| import os
|
|
|
| app = Flask(__name__)
|
|
|
|
|
| DATA_PATH = 'hiring_data_enriched.csv'
|
| MODEL_PATH = 'hiring_model.pkl'
|
|
|
| def get_bias_metrics(df, protected_col, target_col):
|
| groups = sorted(df[protected_col].unique())
|
| metrics = []
|
|
|
| for group in groups:
|
| group_df = df[df[protected_col] == group]
|
| count = len(group_df)
|
| selection_rate = group_df[target_col].mean()
|
| metrics.append({
|
| 'group': str(group),
|
| 'count': int(count),
|
| 'selection_rate': float(selection_rate)
|
| })
|
|
|
|
|
| rates = [m['selection_rate'] for m in metrics]
|
| max_rate = max(rates) if rates else 1
|
| for m in metrics:
|
| m['disparate_impact'] = m['selection_rate'] / max_rate if max_rate > 0 else 0
|
|
|
| return metrics
|
|
|
| def get_intersectional_bias(df):
|
|
|
| intersectional = df.groupby(['Gender', 'Race'])['AI_Decision'].mean().reset_index()
|
| results = []
|
| for _, row in intersectional.iterrows():
|
| results.append({
|
| 'group': f"{row['Gender']} - {row['Race']}",
|
| 'rate': float(row['AI_Decision'])
|
| })
|
| return results
|
|
|
| @app.route('/')
|
| def index():
|
| return render_template('index.html')
|
|
|
| @app.route('/api/stats')
|
| def stats():
|
| if not os.path.exists(DATA_PATH):
|
| return jsonify({'error': 'Data not found'}), 404
|
|
|
| df = pd.read_csv(DATA_PATH)
|
|
|
|
|
| overview_stats = {
|
| 'total_candidates': len(df),
|
| 'ai_hired': int(df['AI_Decision'].sum()),
|
| 'human_hired': int(df['Human_Decision'].sum()),
|
| 'agreement_rate': float(df['Decision_Agreement'].mean() * 100)
|
| }
|
|
|
|
|
| gender_bias = get_bias_metrics(df, 'Gender', 'AI_Decision')
|
| race_bias = get_bias_metrics(df, 'Race', 'AI_Decision')
|
| intersectional = get_intersectional_bias(df)
|
|
|
|
|
| job_breakdown = df.groupby('Job_Category')['AI_Decision'].mean().to_dict()
|
| job_data = [{'category': k, 'rate': float(v)} for k, v in job_breakdown.items()]
|
|
|
| return jsonify({
|
| 'overview': overview_stats,
|
| 'gender_bias': gender_bias,
|
| 'race_bias': race_bias,
|
| 'intersectional': intersectional,
|
| 'job_data': job_data
|
| })
|
|
|
| @app.route('/api/mitigate', methods=['POST'])
|
| def mitigate():
|
|
|
|
|
| df = pd.read_csv(DATA_PATH)
|
|
|
|
|
|
|
| return jsonify({
|
| 'status': 'Mitigation Applied',
|
| 'strategy': 'Dynamic Thresholding (Equal Opportunity)',
|
| 'improvement': '15.4% reduction in disparity'
|
| })
|
|
|
| @app.route('/api/predict', methods=['POST'])
|
| def predict():
|
| data = request.json
|
| try:
|
| years = float(data.get('years', 0))
|
| skill = float(data.get('skill', 0))
|
| job = data.get('job', 'Software Engineer')
|
| edu = data.get('edu', 'Bachelors')
|
|
|
| input_df = pd.DataFrame([{
|
| 'Job_Category': job,
|
| 'Years_Experience': years,
|
| 'Education_Level': edu,
|
| 'Skill_Fit_Score': skill
|
| }])
|
|
|
| if not os.path.exists(MODEL_PATH):
|
| return jsonify({'error': 'Model not found'}), 500
|
|
|
| with open(MODEL_PATH, 'rb') as f:
|
| model = pickle.load(f)
|
|
|
| prediction = model.predict(input_df)[0]
|
| probability = model.predict_proba(input_df)[0][1]
|
|
|
| return jsonify({
|
| 'decision': int(prediction),
|
| 'probability': float(probability)
|
| })
|
| except Exception as e:
|
| return jsonify({'error': str(e)}), 400
|
|
|
| if __name__ == '__main__':
|
| print("Starting HR Hiring Audit System on Port 5001...")
|
| app.run(debug=True, port=5001)
|
|
|