Spaces:
Sleeping
Sleeping
Upload 3 files
Browse files- Dockerfile +9 -0
- app.py +141 -0
- requirements.txt +10 -0
Dockerfile
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.9
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
COPY . /app
|
| 6 |
+
|
| 7 |
+
RUN pip install --no-cache-dir flask gunicorn tensorflow-cpu numpy joblib scikit-learn
|
| 8 |
+
|
| 9 |
+
CMD ["gunicorn", "--bind", "0.0.0.0:7860", "app:app"]
|
app.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Flask, render_template, request, session, redirect, url_for
|
| 2 |
+
import joblib
|
| 3 |
+
import numpy as np
|
| 4 |
+
import tensorflow as tf
|
| 5 |
+
|
| 6 |
+
app = Flask(__name__)
|
| 7 |
+
app.secret_key = "verysecretkey"
|
| 8 |
+
|
| 9 |
+
# Load model & preprocessors
|
| 10 |
+
scaler = joblib.load('preprocessing/scaler.joblib')
|
| 11 |
+
le_gender = joblib.load('preprocessing/le_gender.joblib')
|
| 12 |
+
le_edu = joblib.load('preprocessing/le_edu.joblib')
|
| 13 |
+
le_interest = joblib.load('preprocessing/le_interest.joblib')
|
| 14 |
+
le_personality = joblib.load('preprocessing/le_personality.joblib')
|
| 15 |
+
deep_model = tf.keras.models.load_model('model/deep_mbti_model.h5')
|
| 16 |
+
|
| 17 |
+
mbti_descriptions = {
|
| 18 |
+
'ENTJ': 'The Commander: Strategic leaders, motivated to organize change',
|
| 19 |
+
'INTJ': 'The Mastermind: Analytical problem-solvers, eager to improve systems and processes',
|
| 20 |
+
'ENTP': 'The Visionary: Inspired innovators, seeking new solutions to challenging problems',
|
| 21 |
+
'INTP': 'The Architect: Philosophical innovators, fascinated by logical analysis',
|
| 22 |
+
'ENFJ': 'The Teacher: Idealist organizers, driven to do what is best for humanity',
|
| 23 |
+
'INFJ': 'The Counselor: Creative nurturers, driven by a strong sense of personal integrity',
|
| 24 |
+
'ENFP': 'The Champion: People-centered creators, motivated by possibilities and potential',
|
| 25 |
+
'INFP': 'The Healer: Imaginative idealists, guided by their own values and beliefs',
|
| 26 |
+
'ESTJ': 'The Supervisor: Hardworking traditionalists, taking charge to get things done',
|
| 27 |
+
'ISTJ': 'The Inspector: Responsible organizers, driven to create order out of chaos',
|
| 28 |
+
'ESFJ': 'The Provider: Conscientious helpers, dedicated to their duties to others',
|
| 29 |
+
'ISFJ': 'The Protector: Industrious caretakers, loyal to traditions and institutions',
|
| 30 |
+
'ESTP': 'The Dynamo: Energetic thrillseekers, ready to push boundaries and dive into action',
|
| 31 |
+
'ISTP': 'The Craftsperson: Observant troubleshooters, solving practical problems',
|
| 32 |
+
'ESFP': 'The Entertainer: Vivacious entertainers, loving life and charming those around them',
|
| 33 |
+
'ISFP': 'The Composer: Gentle caretakers, enjoying the moment with low-key enthusiasm'
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
# Mapping for MCQ answers (A/B/C/D) to numeric scores (customize as needed)
|
| 37 |
+
MCQ_MAP = {'A': 10, 'B': 7, 'C': 4, 'D': 1}
|
| 38 |
+
|
| 39 |
+
# MCQ config: 4 MBTI traits, 5 Qs each
|
| 40 |
+
TRAITS = [
|
| 41 |
+
('extroversion', "Extroversion (E/I)", [
|
| 42 |
+
"Are you comfortable with public speaking?",
|
| 43 |
+
"Do you enjoy being the center of attention?",
|
| 44 |
+
"Do you thrive in group settings?",
|
| 45 |
+
"Do you make friends easily at new places?",
|
| 46 |
+
"Do group activities energize you more than being alone?"
|
| 47 |
+
]),
|
| 48 |
+
('sensing', "Sensing vs Intuition (S/N)", [
|
| 49 |
+
"Do you focus more on immediate details than the big picture?",
|
| 50 |
+
"Do you trust experience over theories?",
|
| 51 |
+
"Do you prefer practical applications over abstract concepts?",
|
| 52 |
+
"Do you rely on your five senses rather than gut feelings?",
|
| 53 |
+
"Do you prefer dealing with practical facts rather than imaginative ideas?"
|
| 54 |
+
]),
|
| 55 |
+
('thinking', "Thinking vs Feeling (T/F)", [
|
| 56 |
+
"Do you prioritize logic over emotions when deciding?",
|
| 57 |
+
"Do you value objective truth over social harmony?",
|
| 58 |
+
"Are you comfortable giving critical feedback?",
|
| 59 |
+
"Do you analyze problems objectively rather than considering people's feelings first?",
|
| 60 |
+
"Is it more important for you to be fair than to be compassionate?"
|
| 61 |
+
]),
|
| 62 |
+
('judging', "Judging vs Perceiving (J/P)", [
|
| 63 |
+
"Do you prefer planned activities over spontaneous ones?",
|
| 64 |
+
"Do you enjoy making schedules and to-do lists?",
|
| 65 |
+
"Are you uncomfortable with last-minute changes?",
|
| 66 |
+
"Do you prefer closure and completion over keeping options open?",
|
| 67 |
+
"Do you prefer having a structured plan rather than keeping options open?"
|
| 68 |
+
])
|
| 69 |
+
]
|
| 70 |
+
|
| 71 |
+
# Step 0: Demographics collection
|
| 72 |
+
@app.route('/', methods=['GET', 'POST'])
|
| 73 |
+
def demographics():
|
| 74 |
+
if request.method == 'POST':
|
| 75 |
+
session['demographics'] = {
|
| 76 |
+
'age': request.form['age'],
|
| 77 |
+
'gender': request.form['gender'],
|
| 78 |
+
'education': request.form['education'],
|
| 79 |
+
'interest': request.form['interest']
|
| 80 |
+
}
|
| 81 |
+
return redirect(url_for('mcq', step=1))
|
| 82 |
+
return render_template('index.html',
|
| 83 |
+
gender_classes=le_gender.classes_,
|
| 84 |
+
edu_classes=le_edu.classes_,
|
| 85 |
+
interest_classes=le_interest.classes_)
|
| 86 |
+
|
| 87 |
+
# Step 1-N: MCQ stepper for MBTI traits
|
| 88 |
+
@app.route('/mcq/<int:step>', methods=['GET', 'POST'])
|
| 89 |
+
def mcq(step):
|
| 90 |
+
total_steps = len(TRAITS)
|
| 91 |
+
if request.method == 'POST' and step > 1:
|
| 92 |
+
prev_trait_key = TRAITS[step - 2][0]
|
| 93 |
+
session[prev_trait_key] = [
|
| 94 |
+
request.form.get(f'{prev_trait_key}_q{i}', None) for i in range(1, 6)
|
| 95 |
+
]
|
| 96 |
+
if step > total_steps:
|
| 97 |
+
return redirect(url_for('results'))
|
| 98 |
+
trait_key, trait_label, questions = TRAITS[step - 1]
|
| 99 |
+
return render_template('mcq_step.html',
|
| 100 |
+
trait_key=trait_key,
|
| 101 |
+
trait_label=trait_label,
|
| 102 |
+
questions=questions,
|
| 103 |
+
step=step,
|
| 104 |
+
total_steps=total_steps)
|
| 105 |
+
|
| 106 |
+
# Final: Results calculation and personality prediction
|
| 107 |
+
@app.route('/results')
|
| 108 |
+
def results():
|
| 109 |
+
try:
|
| 110 |
+
data = session.get('demographics', {})
|
| 111 |
+
age = float(data.get('age', 25))
|
| 112 |
+
gender = le_gender.transform([data.get('gender', le_gender.classes_[0])])[0]
|
| 113 |
+
education = le_edu.transform([data.get('education', le_edu.classes_[0])])[0]
|
| 114 |
+
interest = le_interest.transform([data.get('interest', le_interest.classes_[0])])[0]
|
| 115 |
+
|
| 116 |
+
# Collect trait scores, average each over 5 MCQs
|
| 117 |
+
trait_keys = ['extroversion', 'sensing', 'thinking', 'judging']
|
| 118 |
+
trait_scores = []
|
| 119 |
+
for trait_key in trait_keys:
|
| 120 |
+
answers = session.get(trait_key, ['C'] * 5) # default 5 'C'
|
| 121 |
+
numeric_scores = [MCQ_MAP.get(ans, 4) for ans in answers]
|
| 122 |
+
trait_scores.append(float(sum(numeric_scores)) / 5)
|
| 123 |
+
|
| 124 |
+
# Features: [age, gender, education, extroversion, sensing, thinking, judging, interest]
|
| 125 |
+
features = np.array([[age, gender, education] + trait_scores + [interest]])
|
| 126 |
+
features_scaled = scaler.transform(features)
|
| 127 |
+
pred_proba = deep_model.predict(features_scaled)
|
| 128 |
+
pred_class = np.argmax(pred_proba)
|
| 129 |
+
mbti_pred = le_personality.inverse_transform([pred_class])[0]
|
| 130 |
+
class_probs = dict(zip(le_personality.classes_, pred_proba[0].round(3)))
|
| 131 |
+
description = mbti_descriptions.get(mbti_pred, '')
|
| 132 |
+
except Exception as e:
|
| 133 |
+
mbti_pred, class_probs, description = None, None, f"Error: {e}"
|
| 134 |
+
session.clear()
|
| 135 |
+
return render_template('results.html',
|
| 136 |
+
mbti_pred=mbti_pred,
|
| 137 |
+
class_probs=class_probs,
|
| 138 |
+
description=description)
|
| 139 |
+
|
| 140 |
+
if __name__ == '__main__':
|
| 141 |
+
app.run(debug=True)
|
requirements.txt
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Flask==3.0.0
|
| 2 |
+
numpy==1.26.2
|
| 3 |
+
pandas==2.1.2
|
| 4 |
+
joblib==1.3.2
|
| 5 |
+
scikit-learn==1.3.2
|
| 6 |
+
tensorflow==2.15.0
|
| 7 |
+
gunicorn==21.2.0
|
| 8 |
+
Werkzeug==3.0.1
|
| 9 |
+
setuptools>=68.0.0
|
| 10 |
+
wheel>=0.42.0
|