Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -5,7 +5,7 @@ import logging
|
|
| 5 |
from werkzeug.middleware.proxy_fix import ProxyFix
|
| 6 |
|
| 7 |
# ----------------------------------------------------
|
| 8 |
-
# 1. Logging Setup
|
| 9 |
# ----------------------------------------------------
|
| 10 |
logging.basicConfig(
|
| 11 |
level=logging.INFO,
|
|
@@ -13,23 +13,32 @@ logging.basicConfig(
|
|
| 13 |
)
|
| 14 |
|
| 15 |
# ----------------------------------------------------
|
| 16 |
-
# 2. Flask App Initialization
|
| 17 |
# ----------------------------------------------------
|
| 18 |
app = Flask(__name__)
|
|
|
|
|
|
|
| 19 |
app.secret_key = os.environ.get('SECRET_KEY')
|
| 20 |
if not app.secret_key:
|
| 21 |
logging.error("FATAL: SECRET_KEY environment variable is not set. Sessions will fail.")
|
| 22 |
app.secret_key = 'fallback-key-for-dev-only-not-secure'
|
| 23 |
logging.warning("⚠️ Using insecure fallback secret key. Set SECRET_KEY in production.")
|
| 24 |
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
| 26 |
app.config['SESSION_COOKIE_SECURE'] = True
|
| 27 |
-
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
|
| 28 |
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
# ----------------------------------------------------
|
| 32 |
-
# 3. Game Content
|
| 33 |
# ----------------------------------------------------
|
| 34 |
videos = [
|
| 35 |
"static/videos/Animated_Drought_Relief_Video_Creation.mp4",
|
|
@@ -66,7 +75,7 @@ task_scenarios = [
|
|
| 66 |
"choices": [
|
| 67 |
{"action": "🏺 Underground concrete tank with cover", "description": "Safe long-term storage", "consequence": "✅ MASTER STRATEGY! Stored safely. +100 XP", "xp_reward": 100, "icon": "🏛️"},
|
| 68 |
{"action": "🪣 Store in open buckets", "description": "Easy access containers", "consequence": "❌ BREEDING GROUND! Mosquito risk. -20 Health", "xp_reward": 0, "icon": "🦟"},
|
| 69 |
-
{"action": "🌍 Dig a pit and let water sit", "description": "Natural storage", "consequence": "❌ CONTAMINATION RISK! Polluted. -20 Health", "xp_reward": 0, "icon": "
|
| 70 |
]},
|
| 71 |
{"question": "♻️ Dry season! You have 200L left. Neighbor needs help. What do you do?",
|
| 72 |
"choices": [
|
|
@@ -93,7 +102,7 @@ achievements_list = [
|
|
| 93 |
]
|
| 94 |
|
| 95 |
# ----------------------------------------------------
|
| 96 |
-
# 4. Game Logic
|
| 97 |
# ----------------------------------------------------
|
| 98 |
def calculate_level():
|
| 99 |
return min(10, 1 + session.get('xp_points', 0) // 100)
|
|
@@ -112,11 +121,14 @@ def unlock_achievement(condition: str):
|
|
| 112 |
resume_or_initialize()
|
| 113 |
for a in achievements_list:
|
| 114 |
if a["condition"] == condition and a not in session['achievements']:
|
|
|
|
| 115 |
session['achievements'].append(a)
|
|
|
|
| 116 |
session['message'] = f"🏆 Achievement Unlocked: {a['name']} - {a['desc']}"
|
| 117 |
add_xp(50)
|
| 118 |
|
| 119 |
def reset_game_state():
|
|
|
|
| 120 |
session["video_index"] = 0
|
| 121 |
session["feedback"] = ""
|
| 122 |
session["quiz_correct_count"] = 0
|
|
@@ -168,7 +180,7 @@ def select_card_logic(choice_index: int):
|
|
| 168 |
session['health'] = max(0, session['health'] - 20)
|
| 169 |
|
| 170 |
# ----------------------------------------------------
|
| 171 |
-
# 5. Flask Routes
|
| 172 |
# ----------------------------------------------------
|
| 173 |
@app.route('/')
|
| 174 |
def welcome_page():
|
|
@@ -188,7 +200,9 @@ def main_app():
|
|
| 188 |
resume_or_initialize()
|
| 189 |
|
| 190 |
if request.method == 'POST':
|
|
|
|
| 191 |
if 'mark_watched' in request.form:
|
|
|
|
| 192 |
session['video_watched'] = True
|
| 193 |
session['message'] = ""
|
| 194 |
elif 'select_choice' in request.form:
|
|
@@ -206,21 +220,14 @@ def main_app():
|
|
| 206 |
|
| 207 |
idx = session['video_index']
|
| 208 |
context = {
|
| 209 |
-
'current_video_index': idx,
|
| 210 |
-
'total_videos': len(videos),
|
| 211 |
'video_src': url_for('static', filename=videos[idx].replace('static/', '', 1)),
|
| 212 |
-
'video_description': video_descriptions[idx],
|
| 213 |
-
'
|
| 214 |
-
'
|
| 215 |
-
'
|
| 216 |
-
'
|
| 217 |
-
'
|
| 218 |
-
'quiz_correct_count': session['quiz_correct_count'],
|
| 219 |
-
'quiz_attempts': session['quiz_attempts'],
|
| 220 |
-
'video_watched': session['video_watched'],
|
| 221 |
-
'question_answered': session['question_answered'],
|
| 222 |
-
'feedback': session['feedback'],
|
| 223 |
-
'achievements': session['achievements'],
|
| 224 |
'message': session.pop('message', '')
|
| 225 |
}
|
| 226 |
return render_template('main.html', **context)
|
|
@@ -233,13 +240,10 @@ def summary_page():
|
|
| 233 |
if score == total_quizzes and total_quizzes > 0:
|
| 234 |
unlock_achievement("perfect")
|
| 235 |
context = {
|
| 236 |
-
'player_level': session['player_level'],
|
| 237 |
-
'
|
| 238 |
-
'quiz_score': score,
|
| 239 |
-
'total_quizzes': total_quizzes,
|
| 240 |
'accuracy': (score / max(1, session['quiz_attempts'])) * 100,
|
| 241 |
-
'achievements': session['achievements'],
|
| 242 |
-
'current_date': time.strftime("%B %d, %Y"),
|
| 243 |
'message': session.pop('message', '')
|
| 244 |
}
|
| 245 |
return render_template('summary.html', **context)
|
|
@@ -250,7 +254,7 @@ def restart_quest():
|
|
| 250 |
return redirect(url_for('welcome_page'))
|
| 251 |
|
| 252 |
# ----------------------------------------------------
|
| 253 |
-
# 6. Run App
|
| 254 |
# ----------------------------------------------------
|
| 255 |
if __name__ == '__main__':
|
| 256 |
-
app.run(debug=True, port=5000)
|
|
|
|
| 5 |
from werkzeug.middleware.proxy_fix import ProxyFix
|
| 6 |
|
| 7 |
# ----------------------------------------------------
|
| 8 |
+
# 1. Logging Setup (Your code is perfect)
|
| 9 |
# ----------------------------------------------------
|
| 10 |
logging.basicConfig(
|
| 11 |
level=logging.INFO,
|
|
|
|
| 13 |
)
|
| 14 |
|
| 15 |
# ----------------------------------------------------
|
| 16 |
+
# 2. Flask App Initialization (WITH THE FIXES)
|
| 17 |
# ----------------------------------------------------
|
| 18 |
app = Flask(__name__)
|
| 19 |
+
|
| 20 |
+
# A. SECRET_KEY setup (Your logic is fine, just made the error message more explicit)
|
| 21 |
app.secret_key = os.environ.get('SECRET_KEY')
|
| 22 |
if not app.secret_key:
|
| 23 |
logging.error("FATAL: SECRET_KEY environment variable is not set. Sessions will fail.")
|
| 24 |
app.secret_key = 'fallback-key-for-dev-only-not-secure'
|
| 25 |
logging.warning("⚠️ Using insecure fallback secret key. Set SECRET_KEY in production.")
|
| 26 |
|
| 27 |
+
# B. ProxyFix (Enhanced for robustness on Hugging Face)
|
| 28 |
+
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=2, x_proto=1, x_host=1, x_prefix=1)
|
| 29 |
+
|
| 30 |
+
# C. Secure cookie must be true
|
| 31 |
app.config['SESSION_COOKIE_SECURE'] = True
|
|
|
|
| 32 |
|
| 33 |
+
# D. *** THE CRITICAL ONE-SHOT FIX ***
|
| 34 |
+
# Changed from 'Lax' to 'None' to allow the session to work inside the Hugging Face iframe.
|
| 35 |
+
app.config['SESSION_COOKIE_SAMESITE'] = 'None'
|
| 36 |
+
|
| 37 |
+
logging.info("✅ Flask App Initialized with FINAL Production Settings for iframe environments.")
|
| 38 |
+
|
| 39 |
|
| 40 |
# ----------------------------------------------------
|
| 41 |
+
# 3. Game Content (Your code is perfect)
|
| 42 |
# ----------------------------------------------------
|
| 43 |
videos = [
|
| 44 |
"static/videos/Animated_Drought_Relief_Video_Creation.mp4",
|
|
|
|
| 75 |
"choices": [
|
| 76 |
{"action": "🏺 Underground concrete tank with cover", "description": "Safe long-term storage", "consequence": "✅ MASTER STRATEGY! Stored safely. +100 XP", "xp_reward": 100, "icon": "🏛️"},
|
| 77 |
{"action": "🪣 Store in open buckets", "description": "Easy access containers", "consequence": "❌ BREEDING GROUND! Mosquito risk. -20 Health", "xp_reward": 0, "icon": "🦟"},
|
| 78 |
+
{"action": "🌍 Dig a pit and let water sit", "description": "Natural storage", "consequence": "❌ CONTAMINATION RISK! Polluted. -20 Health", "xp_reward": 0, "icon": "️"}
|
| 79 |
]},
|
| 80 |
{"question": "♻️ Dry season! You have 200L left. Neighbor needs help. What do you do?",
|
| 81 |
"choices": [
|
|
|
|
| 102 |
]
|
| 103 |
|
| 104 |
# ----------------------------------------------------
|
| 105 |
+
# 4. Game Logic (Your code is perfect)
|
| 106 |
# ----------------------------------------------------
|
| 107 |
def calculate_level():
|
| 108 |
return min(10, 1 + session.get('xp_points', 0) // 100)
|
|
|
|
| 121 |
resume_or_initialize()
|
| 122 |
for a in achievements_list:
|
| 123 |
if a["condition"] == condition and a not in session['achievements']:
|
| 124 |
+
# Use append to modify the list in place
|
| 125 |
session['achievements'].append(a)
|
| 126 |
+
session.modified = True # Mark session as modified
|
| 127 |
session['message'] = f"🏆 Achievement Unlocked: {a['name']} - {a['desc']}"
|
| 128 |
add_xp(50)
|
| 129 |
|
| 130 |
def reset_game_state():
|
| 131 |
+
session.clear() # Use clear for a full reset
|
| 132 |
session["video_index"] = 0
|
| 133 |
session["feedback"] = ""
|
| 134 |
session["quiz_correct_count"] = 0
|
|
|
|
| 180 |
session['health'] = max(0, session['health'] - 20)
|
| 181 |
|
| 182 |
# ----------------------------------------------------
|
| 183 |
+
# 5. Flask Routes (Your code is perfect)
|
| 184 |
# ----------------------------------------------------
|
| 185 |
@app.route('/')
|
| 186 |
def welcome_page():
|
|
|
|
| 200 |
resume_or_initialize()
|
| 201 |
|
| 202 |
if request.method == 'POST':
|
| 203 |
+
logging.info("ROUTE: /main [POST] - Received POST request. Session is present.")
|
| 204 |
if 'mark_watched' in request.form:
|
| 205 |
+
logging.info("Video marked as watched.")
|
| 206 |
session['video_watched'] = True
|
| 207 |
session['message'] = ""
|
| 208 |
elif 'select_choice' in request.form:
|
|
|
|
| 220 |
|
| 221 |
idx = session['video_index']
|
| 222 |
context = {
|
| 223 |
+
'current_video_index': idx, 'total_videos': len(videos),
|
|
|
|
| 224 |
'video_src': url_for('static', filename=videos[idx].replace('static/', '', 1)),
|
| 225 |
+
'video_description': video_descriptions[idx], 'current_scenario': task_scenarios[idx],
|
| 226 |
+
'player_level': session['player_level'], 'xp_points': session['xp_points'],
|
| 227 |
+
'health': session['health'], 'streak': session['streak'],
|
| 228 |
+
'quiz_correct_count': session['quiz_correct_count'], 'quiz_attempts': session['quiz_attempts'],
|
| 229 |
+
'video_watched': session['video_watched'], 'question_answered': session['question_answered'],
|
| 230 |
+
'feedback': session['feedback'], 'achievements': session['achievements'],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
'message': session.pop('message', '')
|
| 232 |
}
|
| 233 |
return render_template('main.html', **context)
|
|
|
|
| 240 |
if score == total_quizzes and total_quizzes > 0:
|
| 241 |
unlock_achievement("perfect")
|
| 242 |
context = {
|
| 243 |
+
'player_level': session['player_level'], 'xp_points': session['xp_points'],
|
| 244 |
+
'quiz_score': score, 'total_quizzes': total_quizzes,
|
|
|
|
|
|
|
| 245 |
'accuracy': (score / max(1, session['quiz_attempts'])) * 100,
|
| 246 |
+
'achievements': session['achievements'], 'current_date': time.strftime("%B %d, %Y"),
|
|
|
|
| 247 |
'message': session.pop('message', '')
|
| 248 |
}
|
| 249 |
return render_template('summary.html', **context)
|
|
|
|
| 254 |
return redirect(url_for('welcome_page'))
|
| 255 |
|
| 256 |
# ----------------------------------------------------
|
| 257 |
+
# 6. Run App (Your code is perfect)
|
| 258 |
# ----------------------------------------------------
|
| 259 |
if __name__ == '__main__':
|
| 260 |
+
app.run(debug=True, port=5000)
|