binarythinkers89/deploy
0
1import pickle2import pandas as pd3import re4from flask import Flask, request, jsonify, render_template5 6# --- Helper function for text cleaning (as used during training) ---7def clean_text_for_prediction(text):8 """Replicates the text cleaning process for a single string."""9 if isinstance(text, str):10 return ' '.join(re.findall(r'[A-Za-z]+', text)).lower()11 return str(text).lower()12 13loaded_model = None14loaded_le = None15loaded_oEncoder = None16loaded_scaler = None17loaded_feature_columns = []18loaded_view_model = None19loaded_view_le = None20try:21 with open('feature_columns.pkl', 'rb') as file:22 loaded_feature_columns = pickle.load(file)23except Exception:24 loaded_feature_columns = []25try:26 with open('label_encoder.pkl', 'rb') as file:27 loaded_le = pickle.load(file)28except Exception:29 loaded_le = None30try:31 with open('ordinal_encoder.pkl', 'rb') as file:32 loaded_oEncoder = pickle.load(file)33except Exception:34 loaded_oEncoder = None35try:36 with open('standard_scaler.pkl', 'rb') as file:37 loaded_scaler = pickle.load(file)38except Exception:39 loaded_scaler = None40try:41 with open('stacking_ensemble_model.pkl', 'rb') as file:42 loaded_model = pickle.load(file)43except Exception:44 loaded_model = None45try:46 with open('best_political_view_model.pkl', 'rb') as file:47 loaded_view_model = pickle.load(file)48except Exception:49 loaded_view_model = None50try:51 with open('best_political_view_label_encoder.pkl', 'rb') as file:52 loaded_view_le = pickle.load(file)53except Exception:54 loaded_view_le = None55 56# --- Initialize Flask Application ---57app = Flask(__name__, template_folder='.')58 59# --- Define Home Route ---60@app.route('/')61def home():62 return render_template('view.html')63 64@app.route('/view')65def view():66 return render_template('view.html')67 68@app.route('/schema', methods=['GET'])69def schema():70 cats = {}71 if loaded_oEncoder and loaded_feature_columns:72 try:73 categories_list = loaded_oEncoder.categories_74 for k, v in zip(loaded_feature_columns, categories_list):75 cats[k] = [str(x) for x in list(v)]76 except Exception:77 cats = {}78 return jsonify({"feature_columns": loaded_feature_columns, "categories": cats})79 80# --- Define Prediction Route ---81@app.route('/predict', methods=['POST'])82def predict():83 if not loaded_feature_columns:84 return jsonify({"error": "Feature columns not loaded"}), 50385 if not request.is_json:86 return jsonify({"error": "Request must be JSON"}), 40087 88 new_data_raw = request.get_json()89 90 # Ensure input data keys match feature columns (case-insensitive for robustness, though training used specific casing)91 processed_input = {col: new_data_raw.get(col, "") for col in loaded_feature_columns}92 93 # Convert new_data to DataFrame, ensuring correct column order94 # Using a list of dictionaries to handle single row for DataFrame creation, which preserves column order from loaded_feature_columns95 new_df = pd.DataFrame([processed_input], columns=loaded_feature_columns)96 97 # Apply text cleaning preprocessing steps98 for col in new_df.columns:99 # Assuming 'F1' is the age column and should not be regex cleaned100 if col != 'F1': 101 new_df[col] = new_df[col].apply(clean_text_for_prediction)102 else:103 # Ensure 'F1' is lowercase string for consistent ordinal encoding if it contains non-numeric parts104 new_df[col] = new_df[col].astype(str).str.lower()105 106 if loaded_model and loaded_oEncoder and loaded_scaler and loaded_le:107 try:108 new_data_encoded = loaded_oEncoder.transform(new_df)109 except ValueError as e:110 return jsonify({"error": f"OrdinalEncoder transformation failed: {e}. Check input categories."}), 400111 new_data_scaled = loaded_scaler.transform(new_data_encoded)112 prediction_numerical = loaded_model.predict(new_data_scaled)113 predicted_label = loaded_le.inverse_transform(prediction_numerical)114 return jsonify({'predicted_engagement': predicted_label[0]})115 else:116 if loaded_le is not None and hasattr(loaded_le, 'classes_') and len(loaded_le.classes_) > 0:117 fallback_label = loaded_le.classes_[0]118 else:119 fallback_label = "unavailable"120 return jsonify({'predicted_engagement': fallback_label, 'note': 'fallback'}), 200121 122@app.route('/predict_debug', methods=['POST'])123def predict_debug():124 ready = all([loaded_feature_columns, loaded_model, loaded_oEncoder, loaded_scaler, loaded_le])125 if not request.is_json:126 return jsonify({"error": "Request must be JSON"}), 400127 raw = request.get_json()128 processed = {col: raw.get(col, "") for col in loaded_feature_columns} if loaded_feature_columns else raw129 df = pd.DataFrame([processed], columns=loaded_feature_columns) if loaded_feature_columns else pd.DataFrame([processed])130 for col in df.columns:131 if col != 'F1':132 df[col] = df[col].apply(clean_text_for_prediction)133 else:134 df[col] = df[col].astype(str).str.lower()135 debug = {136 'components': {137 'has_feature_columns': bool(loaded_feature_columns),138 'has_model': loaded_model is not None,139 'has_ordinal': loaded_oEncoder is not None,140 'has_scaler': loaded_scaler is not None,141 'has_label_encoder': loaded_le is not None,142 },143 'feature_columns_count': len(loaded_feature_columns) if loaded_feature_columns else 0,144 'cleaned_row': df.to_dict(orient='records')[0]145 }146 if ready:147 try:148 encoded = loaded_oEncoder.transform(df)149 scaled = loaded_scaler.transform(encoded)150 pred_num = loaded_model.predict(scaled)151 pred_label = loaded_le.inverse_transform(pred_num)152 debug['encoded'] = encoded[0].tolist()153 debug['scaled'] = scaled[0].tolist()154 debug['prediction_numerical'] = int(pred_num[0]) if hasattr(pred_num, '__iter__') else pred_num155 debug['prediction_label'] = pred_label[0]156 return jsonify(debug)157 except Exception as e:158 debug['error'] = str(e)159 return jsonify(debug), 500160 else:161 return jsonify(debug), 200162 163@app.route('/predict_view', methods=['POST'])164def predict_view():165 if not loaded_feature_columns:166 return jsonify({"error": "Feature columns not loaded"}), 503167 if not request.is_json:168 return jsonify({"error": "Request must be JSON"}), 400169 new_data_raw = request.get_json()170 processed_input = {col: new_data_raw.get(col, "") for col in loaded_feature_columns}171 new_df = pd.DataFrame([processed_input], columns=loaded_feature_columns)172 for col in new_df.columns:173 if col != 'F1':174 new_df[col] = new_df[col].apply(clean_text_for_prediction)175 else:176 new_df[col] = new_df[col].astype(str).str.lower()177 if loaded_view_model and loaded_oEncoder and loaded_scaler:178 try:179 new_data_encoded = loaded_oEncoder.transform(new_df)180 except ValueError as e:181 return jsonify({"error": f"OrdinalEncoder transformation failed: {e}. Check input categories."}), 400182 new_data_scaled = loaded_scaler.transform(new_data_encoded)183 expected_n = getattr(loaded_view_model, 'n_features_in_', None)184 if isinstance(expected_n, int) and expected_n > 0 and new_data_scaled.shape[1] != expected_n:185 new_data_scaled = new_data_scaled[:, :expected_n]186 yhat = loaded_view_model.predict(new_data_scaled)187 if loaded_view_le:188 try:189 yhat_label = loaded_view_le.inverse_transform(yhat)190 return jsonify({'predicted_view': str(yhat_label[0])})191 except Exception:192 return jsonify({'predicted_view': str(yhat[0])})193 else:194 classes_attr = getattr(loaded_view_model, 'classes_', None)195 if classes_attr is not None:196 mapping = None197 try:198 as_str = [str(c) for c in list(classes_attr)]199 if set(as_str) == set(['0','1','2']):200 mapping = {'0': 'active', '1': 'moderately_interested', '2': 'not_interested'}201 except Exception:202 mapping = None203 if mapping:204 key = str(yhat[0])205 return jsonify({'predicted_view': mapping.get(key, key)})206 return jsonify({'predicted_view': str(yhat[0])})207 else:208 return jsonify({'predicted_view': 'unavailable', 'note': 'fallback'}), 200209 210# --- Run the Flask application ---211if __name__ == '__main__':212 app.run(debug=True, host='0.0.0.0', port=7860)