File size: 3,867 Bytes
a543cdf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import joblib
import pandas as pd
from flask import Flask, jsonify, request

# Initialise the Flask app and load the serialized pipeline (preprocessing + model) once at startup
app = Flask("ExtraaLearn Lead Conversion Predictor")
model = joblib.load("extraalearn_lead_model_v1_0.joblib")

# Raw input features the pipeline expects, with their allowed values where categorical
FEATURES = ["age", "current_occupation", "first_interaction", "profile_completed", "website_visits",
            "time_spent_on_website", "page_views_per_visit", "last_activity", "print_media_type1",
            "print_media_type2", "digital_media", "educational_channels", "referral"]
NUMERIC = {"age": int, "website_visits": int, "time_spent_on_website": int, "page_views_per_visit": float}
CATEGORIES = {
    "current_occupation": ["Professional", "Unemployed", "Student"],
    "first_interaction": ["Website", "Mobile App"],
    "profile_completed": ["Low", "Medium", "High"],
    "last_activity": ["Email Activity", "Phone Activity", "Website Activity"],
    **{flag: ["Yes", "No"] for flag in ["print_media_type1", "print_media_type2", "digital_media",
                                        "educational_channels", "referral"]},
}


def validate(record):
    """Check that a lead record has every feature with a valid value; return a list of errors."""
    errors = []
    for f in FEATURES:
        if f not in record:
            errors.append(f"missing field '{f}'")
        elif f in NUMERIC:
            try:
                if NUMERIC[f](record[f]) < 0:
                    errors.append(f"'{f}' must be non-negative")
            except (TypeError, ValueError):
                errors.append(f"'{f}' must be numeric")
        elif record[f] not in CATEGORIES[f]:
            errors.append(f"'{f}' must be one of {CATEGORIES[f]}")
    return errors


def to_frame(records):
    """Build a model-ready DataFrame with the right column order and numeric types."""
    df = pd.DataFrame(records)[FEATURES]
    for f, cast in NUMERIC.items():
        df[f] = df[f].astype(cast)
    return df


@app.get("/")
def home():
    """Health check."""
    return "Welcome to the ExtraaLearn Lead Conversion Prediction API!"


@app.post("/v1/lead")
def predict_lead():
    """Predict the conversion of a single lead sent as a JSON object."""
    record = request.get_json(silent=True)
    if not isinstance(record, dict):
        return jsonify({"error": "Request body must be a JSON object with the lead's features"}), 400
    errors = validate(record)
    if errors:
        return jsonify({"error": "Invalid input", "details": errors}), 400

    df = to_frame([record])
    probability = float(model.predict_proba(df)[0, 1])
    prediction = int(model.predict(df)[0])
    return jsonify({
        "prediction": prediction,
        "prediction_label": "Likely to convert" if prediction == 1 else "Unlikely to convert",
        "conversion_probability": round(probability, 4),
    })


@app.post("/v1/leadbatch")
def predict_lead_batch():
    """Predict the conversion of every lead in an uploaded CSV file (form field 'file')."""
    if "file" not in request.files:
        return jsonify({"error": "Upload a CSV file in the form field 'file'"}), 400
    batch = pd.read_csv(request.files["file"])
    missing = [f for f in FEATURES if f not in batch.columns]
    if missing:
        return jsonify({"error": f"CSV is missing columns: {missing}"}), 400

    df = to_frame(batch)
    ids = batch["ID"].astype(str).tolist() if "ID" in batch.columns else [str(i) for i in range(len(batch))]
    results = [
        {"ID": i, "prediction": int(p), "conversion_probability": round(float(pr), 4)}
        for i, p, pr in zip(ids, model.predict(df), model.predict_proba(df)[:, 1])
    ]
    return jsonify(results)


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=7860, debug=False)