Spaces:
Running
Running
File size: 3,758 Bytes
73633b5 | 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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | # βββββββββββββββββββββββββββββββββββββββββββββ
# app/components.py
# Reusable Streamlit UI building blocks.
# Keeps main.py clean and focused on flow logic.
# βββββββββββββββββββββββββββββββββββββββββββββ
import streamlit as st
from src.mcq_builder import MCQ
def render_question_card(mcq: MCQ, index: int) -> str | None:
"""
Render a question with labelled radio button options.
Returns the selected option label ("A"/"B"/"C"/"D") or None.
"""
st.markdown(f"### Q{index + 1}. {mcq.question}")
# Build labelled options: ["A. Paris", "B. London", ...]
labelled_options = [f"{chr(65+i)}. {opt}" for i, opt in enumerate(mcq.options)]
# Restore previous selection if user came back to this question
prev_index = st.session_state.user_answers[index]
default = prev_index if prev_index >= 0 else 0
selected = st.radio(
label = "Select your answer:",
options = labelled_options,
index = default,
key = f"q_{index}",
label_visibility = "collapsed",
)
# Return just the letter ("A", "B", etc.)
return selected[0] if selected else None
def render_result_card(result: dict, question_num: int):
"""
Render a single question's result with colour coding.
Green = correct, Red = wrong.
"""
is_correct = result["is_correct"]
icon = "β
" if is_correct else "β"
color = "#d4edda" if is_correct else "#f8d7da"
border = "#28a745" if is_correct else "#dc3545"
with st.container():
st.markdown(
f"""
<div style="
background-color: {color};
border-left: 4px solid {border};
padding: 12px 16px;
border-radius: 6px;
margin-bottom: 12px;
">
<b>{icon} Q{question_num}: {result['question']}</b>
</div>
""",
unsafe_allow_html=True,
)
col1, col2 = st.columns(2)
with col1:
st.write(f"**Your answer:** {result['your_answer']}")
with col2:
if not is_correct:
st.write(f"**Correct answer:** {result['correct_answer']}")
else:
st.write("**Correct!**")
with st.expander("See explanation"):
st.info(result["explanation"])
def render_score_summary(result: dict):
"""
Render the score banner at the top of the results screen.
"""
score = result["score"]
total = result["total"]
percentage = result["percentage"]
feedback = result["feedback"]
# Choose colour based on score
if percentage >= 80:
color = "#d4edda"; border = "#28a745"
elif percentage >= 60:
color = "#fff3cd"; border = "#ffc107"
else:
color = "#f8d7da"; border = "#dc3545"
st.markdown(
f"""
<div style="
background-color: {color};
border: 2px solid {border};
border-radius: 10px;
padding: 20px 24px;
text-align: center;
">
<h2 style="margin:0">{score} / {total}</h2>
<h4 style="margin:4px 0">{percentage}%</h4>
<p style="margin:0; color: #555">{feedback}</p>
</div>
""",
unsafe_allow_html=True,
)
# Metric columns for quick glance
st.markdown("<br>", unsafe_allow_html=True)
c1, c2, c3 = st.columns(3)
c1.metric("Correct", score)
c2.metric("Wrong", total - score)
c3.metric("Score", f"{percentage}%") |