Spaces:
Running
Running
Update app.py (#87)
Browse files- Update app.py (81217ddd27c445a85a4c7b4865d6b669678af7e5)
app.py
CHANGED
|
@@ -785,18 +785,61 @@ if 'active_game' not in st.session_state:
|
|
| 785 |
|
| 786 |
if 'username' not in st.session_state:
|
| 787 |
st.session_state['username'] = "Guest" # temporary default username
|
| 788 |
-
|
| 789 |
-
|
| 790 |
-
|
| 791 |
-
|
| 792 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 793 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 794 |
if not gid or not uname:
|
| 795 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 796 |
return
|
| 797 |
|
| 798 |
-
|
| 799 |
-
questions = st.session_state.get('game_questions') or st.session_state.get('game_data', {}).get('questions', [])
|
| 800 |
if not questions:
|
| 801 |
st.error("No questions loaded.")
|
| 802 |
return
|
|
@@ -805,156 +848,74 @@ def play_page():
|
|
| 805 |
if 'question_started_at' not in st.session_state or st.session_state['question_started_at'] is None:
|
| 806 |
st.session_state['question_started_at'] = time.time()
|
| 807 |
|
| 808 |
-
# Current question index
|
| 809 |
idx = st.session_state.get('current_index', 0)
|
| 810 |
if idx >= len(questions):
|
| 811 |
-
st.success("All
|
| 812 |
return
|
| 813 |
|
| 814 |
-
# Get current question safely (dict or list)
|
| 815 |
q = questions[idx]
|
| 816 |
-
if isinstance(q, dict):
|
| 817 |
-
question_text = q.get("question", "")
|
| 818 |
-
options = q.get("options", [])
|
| 819 |
-
elif isinstance(q, list):
|
| 820 |
-
# Assumes format: [question, options_list, answer]
|
| 821 |
-
question_text = q[0]
|
| 822 |
-
options = q[1]
|
| 823 |
-
else:
|
| 824 |
-
st.error(f"Invalid question format at index {idx}.")
|
| 825 |
-
return
|
| 826 |
-
|
| 827 |
st.subheader(f"Question {idx+1}/{len(questions)}")
|
| 828 |
-
st.write(
|
| 829 |
|
| 830 |
-
# Timer
|
| 831 |
elapsed = int(time.time() - st.session_state['question_started_at'])
|
| 832 |
time_limit = 15
|
| 833 |
st.markdown(f"**Time left:** {max(0, time_limit - elapsed)} seconds")
|
| 834 |
|
| 835 |
-
|
| 836 |
-
choice = st.radio("Choose an answer:", options, key=f"choice_{idx}")
|
| 837 |
|
| 838 |
col1, col2 = st.columns(2)
|
| 839 |
|
| 840 |
-
# Next button
|
| 841 |
with col1:
|
| 842 |
-
if st.button("Next"):
|
| 843 |
taken = time.time() - st.session_state['question_started_at']
|
| 844 |
-
|
| 845 |
answers = st.session_state.get('answers', [""] * len(questions))
|
| 846 |
times = st.session_state.get('answer_times', [None] * len(questions))
|
| 847 |
-
|
| 848 |
answers[idx] = choice
|
| 849 |
times[idx] = taken
|
| 850 |
-
|
| 851 |
st.session_state['answers'] = answers
|
| 852 |
st.session_state['answer_times'] = times
|
| 853 |
st.session_state['current_index'] = idx + 1
|
| 854 |
-
st.session_state['question_started_at'] = time.time()
|
| 855 |
-
|
| 856 |
st.experimental_rerun()
|
| 857 |
|
| 858 |
-
# Submit button on last question
|
| 859 |
with col2:
|
| 860 |
-
if idx == len(questions)
|
| 861 |
-
if st.button("Submit All Answers"):
|
| 862 |
-
answers = st.session_state.get('answers', [""]
|
| 863 |
-
times = st.session_state.get('answer_times', [None]
|
| 864 |
-
|
| 865 |
-
# Save last answer
|
| 866 |
-
taken = time.time() - st.session_state.get('question_started_at', time.time())
|
| 867 |
answers[idx] = choice
|
| 868 |
-
times[idx] =
|
| 869 |
-
|
| 870 |
st.session_state['answers'] = answers
|
| 871 |
st.session_state['answer_times'] = times
|
| 872 |
|
| 873 |
-
# Compute score
|
| 874 |
-
score
|
| 875 |
-
|
| 876 |
|
| 877 |
-
#
|
| 878 |
-
|
| 879 |
-
|
|
|
|
|
|
|
| 880 |
"score": score,
|
| 881 |
-
"
|
| 882 |
-
"
|
| 883 |
-
"
|
| 884 |
-
"
|
| 885 |
-
"questions": " || ".join([q[0] if isinstance(q, list) else q.get("question","") for q in questions]),
|
| 886 |
-
"answers": " || ".join([str(a) for a in answers]),
|
| 887 |
-
"correct_flags": " || ".join(flags)
|
| 888 |
}
|
| 889 |
-
|
| 890 |
|
| 891 |
-
#
|
| 892 |
-
|
| 893 |
-
|
| 894 |
-
|
| 895 |
-
|
| 896 |
-
|
| 897 |
-
|
| 898 |
-
|
| 899 |
-
# Auto-close game if enabled
|
| 900 |
-
games = unified_get("games") or {}
|
| 901 |
-
if st.session_state.get('game_data', {}).get('auto_close', True):
|
| 902 |
-
if gid in games:
|
| 903 |
-
games[gid]['closed'] = True
|
| 904 |
-
games[gid]['closed_at'] = now
|
| 905 |
-
unified_set("games", games)
|
| 906 |
-
|
| 907 |
-
st.success(f"Submitted! Score: {score}")
|
| 908 |
st.balloons()
|
| 909 |
st.session_state['last_score'] = score
|
| 910 |
st.session_state['last_game'] = gid
|
| 911 |
st.session_state['current_index'] = 0
|
| 912 |
-
|
| 913 |
-
# ---------- PLAY PAGE ----------
|
| 914 |
-
|
| 915 |
-
# Create game
|
| 916 |
-
def create_game(topics=None, num_questions=5, auto_close=True, ai_topic=None):
|
| 917 |
-
topics = topics or []
|
| 918 |
-
|
| 919 |
-
gid = f"GAME{int(time.time())}{random.randint(100,999)}"
|
| 920 |
-
game_dict = {
|
| 921 |
-
"game_id": gid,
|
| 922 |
-
"host": st.session_state.get('username', 'Host'),
|
| 923 |
-
"topics": topics,
|
| 924 |
-
"num_questions": num_questions,
|
| 925 |
-
"auto_close_on_submit": auto_close,
|
| 926 |
-
"ai_topic": ai_topic,
|
| 927 |
-
"questions": [],
|
| 928 |
-
"created_at": now_iso(),
|
| 929 |
-
"closed": False
|
| 930 |
-
}
|
| 931 |
-
|
| 932 |
-
# Add AI-generated questions if provided
|
| 933 |
-
if ai_topic:
|
| 934 |
-
ai_questions = generate_ai_questions(ai_topic, num_questions=num_questions)
|
| 935 |
-
if ai_questions:
|
| 936 |
-
game_dict["questions"].extend(ai_questions)
|
| 937 |
-
else:
|
| 938 |
-
st.warning(f"No AI questions generated for topic '{ai_topic}'. Using static topics if any.")
|
| 939 |
-
|
| 940 |
-
# Add static questions if topics provided
|
| 941 |
-
for topic in topics:
|
| 942 |
-
qs = questions_db.get(topic, [])[:num_questions]
|
| 943 |
-
game_dict["questions"].extend(qs)
|
| 944 |
-
|
| 945 |
-
# Shuffle questions
|
| 946 |
-
random.shuffle(game_dict["questions"])
|
| 947 |
-
|
| 948 |
-
# Save
|
| 949 |
-
games = unified_get("games") or {}
|
| 950 |
-
games[gid] = game_dict
|
| 951 |
-
unified_set("games", games)
|
| 952 |
-
st.session_state['games'] = games
|
| 953 |
-
|
| 954 |
-
return gid
|
| 955 |
-
|
| 956 |
-
|
| 957 |
-
|
| 958 |
# Join game
|
| 959 |
def join_game_page():
|
| 960 |
st.header("Join Game")
|
|
|
|
| 785 |
|
| 786 |
if 'username' not in st.session_state:
|
| 787 |
st.session_state['username'] = "Guest" # temporary default username
|
| 788 |
+
|
| 789 |
+
def create_game(host=None, topics=[], num_questions=5, auto_close=True, ai_topic=None):
|
| 790 |
+
games = unified_get("games") or {}
|
| 791 |
+
gid = f"GAME{int(time.time())}" # simple unique id
|
| 792 |
+
host = host or st.session_state.get("username", "Host")
|
| 793 |
+
|
| 794 |
+
# 🔹 Get questions
|
| 795 |
+
questions = []
|
| 796 |
+
|
| 797 |
+
# 1️⃣ AI questions if topic given
|
| 798 |
+
if ai_topic:
|
| 799 |
+
ai_questions = generate_ai_questions(ai_topic, num_questions=num_questions)
|
| 800 |
+
if ai_questions:
|
| 801 |
+
questions = ai_questions
|
| 802 |
+
|
| 803 |
+
# 2️⃣ Fallback to static questions_db
|
| 804 |
+
if not questions:
|
| 805 |
+
for topic in topics:
|
| 806 |
+
qs = questions_db.get(topic.lower(), questions_db.get("default", []))
|
| 807 |
+
questions.extend(qs[:num_questions])
|
| 808 |
+
|
| 809 |
+
# Ensure each question is a dict with keys: question, options, answer
|
| 810 |
+
questions = [{"question": q["question"], "options": q["options"], "answer": q["answer"]} for q in questions]
|
| 811 |
+
|
| 812 |
+
# 🔹 Store game
|
| 813 |
+
games[gid] = {
|
| 814 |
+
"game_id": gid,
|
| 815 |
+
"host": host,
|
| 816 |
+
"topics": topics,
|
| 817 |
+
"questions": questions,
|
| 818 |
+
"created_at": now_iso(),
|
| 819 |
+
"closed": False,
|
| 820 |
+
"auto_close": auto_close
|
| 821 |
+
}
|
| 822 |
+
unified_set("games", games)
|
| 823 |
+
st.success(f"Game created: {gid} with {len(questions)} questions.")
|
| 824 |
+
return gid
|
| 825 |
|
| 826 |
+
# -------------------------
|
| 827 |
+
# PLAY PAGE
|
| 828 |
+
# -------------------------
|
| 829 |
+
def play_page():
|
| 830 |
+
gid = st.session_state.get("active_game_id")
|
| 831 |
+
uname = st.session_state.get("username")
|
| 832 |
if not gid or not uname:
|
| 833 |
+
st.error("No active game or username found. Please join or create a game first.")
|
| 834 |
+
return
|
| 835 |
+
|
| 836 |
+
games = unified_get("games") or {}
|
| 837 |
+
game = games.get(gid)
|
| 838 |
+
if not game:
|
| 839 |
+
st.error("Game not found!")
|
| 840 |
return
|
| 841 |
|
| 842 |
+
questions = st.session_state.get("game_questions") or game.get("questions", [])
|
|
|
|
| 843 |
if not questions:
|
| 844 |
st.error("No questions loaded.")
|
| 845 |
return
|
|
|
|
| 848 |
if 'question_started_at' not in st.session_state or st.session_state['question_started_at'] is None:
|
| 849 |
st.session_state['question_started_at'] = time.time()
|
| 850 |
|
|
|
|
| 851 |
idx = st.session_state.get('current_index', 0)
|
| 852 |
if idx >= len(questions):
|
| 853 |
+
st.success("All done — submit your answers!")
|
| 854 |
return
|
| 855 |
|
|
|
|
| 856 |
q = questions[idx]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 857 |
st.subheader(f"Question {idx+1}/{len(questions)}")
|
| 858 |
+
st.write(q["question"])
|
| 859 |
|
|
|
|
| 860 |
elapsed = int(time.time() - st.session_state['question_started_at'])
|
| 861 |
time_limit = 15
|
| 862 |
st.markdown(f"**Time left:** {max(0, time_limit - elapsed)} seconds")
|
| 863 |
|
| 864 |
+
choice = st.radio("Choose an answer:", q["options"], key=f"choice_{idx}")
|
|
|
|
| 865 |
|
| 866 |
col1, col2 = st.columns(2)
|
| 867 |
|
|
|
|
| 868 |
with col1:
|
| 869 |
+
if st.button("Next", key=f"next_{idx}"):
|
| 870 |
taken = time.time() - st.session_state['question_started_at']
|
|
|
|
| 871 |
answers = st.session_state.get('answers', [""] * len(questions))
|
| 872 |
times = st.session_state.get('answer_times', [None] * len(questions))
|
|
|
|
| 873 |
answers[idx] = choice
|
| 874 |
times[idx] = taken
|
|
|
|
| 875 |
st.session_state['answers'] = answers
|
| 876 |
st.session_state['answer_times'] = times
|
| 877 |
st.session_state['current_index'] = idx + 1
|
| 878 |
+
st.session_state['question_started_at'] = time.time()
|
|
|
|
| 879 |
st.experimental_rerun()
|
| 880 |
|
|
|
|
| 881 |
with col2:
|
| 882 |
+
if idx == len(questions)-1:
|
| 883 |
+
if st.button("Submit All Answers", key=f"submit_{idx}"):
|
| 884 |
+
answers = st.session_state.get('answers', [""]*len(questions))
|
| 885 |
+
times = st.session_state.get('answer_times', [None]*len(questions))
|
|
|
|
|
|
|
|
|
|
| 886 |
answers[idx] = choice
|
| 887 |
+
times[idx] = time.time() - st.session_state.get('question_started_at', time.time())
|
|
|
|
| 888 |
st.session_state['answers'] = answers
|
| 889 |
st.session_state['answer_times'] = times
|
| 890 |
|
| 891 |
+
# Compute score
|
| 892 |
+
score = sum([1 for i,q in enumerate(questions) if answers[i] == q["answer"]])
|
| 893 |
+
percentage = int(score / len(questions) * 100)
|
| 894 |
|
| 895 |
+
# Update players
|
| 896 |
+
players = unified_get("players") or {}
|
| 897 |
+
players.setdefault(gid, {})
|
| 898 |
+
players[gid][uname] = {
|
| 899 |
+
"submitted": True,
|
| 900 |
"score": score,
|
| 901 |
+
"percentage": percentage,
|
| 902 |
+
"answers": answers,
|
| 903 |
+
"avatar": st.session_state.get("avatar", "🎮"),
|
| 904 |
+
"timestamp": now_iso()
|
|
|
|
|
|
|
|
|
|
| 905 |
}
|
| 906 |
+
unified_set("players", players)
|
| 907 |
|
| 908 |
+
# Auto-close game
|
| 909 |
+
if game.get('auto_close', True):
|
| 910 |
+
games[gid]['closed'] = True
|
| 911 |
+
games[gid]['closed_at'] = now_iso()
|
| 912 |
+
unified_set("games", games)
|
| 913 |
+
|
| 914 |
+
st.success(f"Submitted! Score: {score} / {len(questions)} ({percentage}%)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 915 |
st.balloons()
|
| 916 |
st.session_state['last_score'] = score
|
| 917 |
st.session_state['last_game'] = gid
|
| 918 |
st.session_state['current_index'] = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 919 |
# Join game
|
| 920 |
def join_game_page():
|
| 921 |
st.header("Join Game")
|