Update app.py
Browse files
app.py
CHANGED
|
@@ -88,6 +88,12 @@ APP_CSS = """
|
|
| 88 |
radial-gradient(circle at 90% 0%, #d7f4e3 0, transparent 26%),
|
| 89 |
linear-gradient(140deg, var(--bg-1), var(--bg-2));
|
| 90 |
color: var(--ink);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
}
|
| 92 |
|
| 93 |
.gradio-container,
|
|
@@ -113,11 +119,51 @@ APP_CSS = """
|
|
| 113 |
border: 1px solid #cfd6c6 !important;
|
| 114 |
}
|
| 115 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
.gradio-container ::placeholder {
|
| 117 |
color: #6b7280 !important;
|
| 118 |
opacity: 1;
|
| 119 |
}
|
| 120 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
.hero {
|
| 122 |
border: 1px solid var(--line);
|
| 123 |
background: linear-gradient(120deg, #fffdf5 0%, #f4fffa 100%);
|
|
@@ -415,6 +461,35 @@ def create_room(teacher_pin: str, budget_per_team: int = 20) -> Tuple[str, str]:
|
|
| 415 |
return code, f"Room created: {code}"
|
| 416 |
|
| 417 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 418 |
def join_room(room_code: str, team_name: str) -> Tuple[dict, str]:
|
| 419 |
room_code = room_code.strip().upper()
|
| 420 |
team_name = team_name.strip()
|
|
@@ -837,6 +912,16 @@ with gr.Blocks(
|
|
| 837 |
<h1>AI Café Challenge</h1>
|
| 838 |
<p>Run your campus café like a data team: unlock evidence, test decisions, and iterate with tutor feedback.</p>
|
| 839 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 840 |
"""
|
| 841 |
)
|
| 842 |
|
|
@@ -847,6 +932,7 @@ with gr.Blocks(
|
|
| 847 |
room_in = gr.Textbox(label="Room code", placeholder="ROOM-1234")
|
| 848 |
team_in = gr.Textbox(label="Team name", placeholder="e.g., Team Orion")
|
| 849 |
join_btn = gr.Button("Join")
|
|
|
|
| 850 |
|
| 851 |
join_status = gr.Markdown("Not joined.", elem_classes=["block-panel"])
|
| 852 |
header = gr.Markdown("")
|
|
@@ -909,9 +995,15 @@ with gr.Blocks(
|
|
| 909 |
sess, msg = join_room(room_code, team_name)
|
| 910 |
return sess, msg
|
| 911 |
|
|
|
|
|
|
|
|
|
|
| 912 |
join_btn.click(_join, [room_in, team_in], [session, join_status]).then(
|
| 913 |
ui_refresh, [session], [header, score_table, event_text]
|
| 914 |
)
|
|
|
|
|
|
|
|
|
|
| 915 |
|
| 916 |
refresh_btn.click(ui_refresh, [session], [header, score_table, event_text])
|
| 917 |
|
|
|
|
| 88 |
radial-gradient(circle at 90% 0%, #d7f4e3 0, transparent 26%),
|
| 89 |
linear-gradient(140deg, var(--bg-1), var(--bg-2));
|
| 90 |
color: var(--ink);
|
| 91 |
+
--body-text-color: var(--ink);
|
| 92 |
+
--body-text-color-subdued: #2b303a;
|
| 93 |
+
--block-title-text-color: var(--ink);
|
| 94 |
+
--block-label-text-color: var(--ink);
|
| 95 |
+
--input-text-color: var(--ink);
|
| 96 |
+
--button-secondary-text-color: var(--ink);
|
| 97 |
}
|
| 98 |
|
| 99 |
.gradio-container,
|
|
|
|
| 119 |
border: 1px solid #cfd6c6 !important;
|
| 120 |
}
|
| 121 |
|
| 122 |
+
.gradio-container select option,
|
| 123 |
+
.gradio-container option {
|
| 124 |
+
color: #111827 !important;
|
| 125 |
+
background: #ffffff !important;
|
| 126 |
+
opacity: 1 !important;
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
.gradio-container ::placeholder {
|
| 130 |
color: #6b7280 !important;
|
| 131 |
opacity: 1;
|
| 132 |
}
|
| 133 |
|
| 134 |
+
/* Force high contrast for muted/inactive Gradio states */
|
| 135 |
+
.gradio-container .tab-nav button,
|
| 136 |
+
.gradio-container .tab-nav button[aria-selected="false"],
|
| 137 |
+
.gradio-container .tab-nav button[aria-selected="true"],
|
| 138 |
+
.gradio-container .secondary,
|
| 139 |
+
.gradio-container .secondary * ,
|
| 140 |
+
.gradio-container [class*="dropdown"] *,
|
| 141 |
+
.gradio-container [role="listbox"],
|
| 142 |
+
.gradio-container [role="listbox"] *,
|
| 143 |
+
.gradio-container [role="option"],
|
| 144 |
+
.gradio-container [role="option"] *,
|
| 145 |
+
.gradio-container .gr-form .form,
|
| 146 |
+
.gradio-container .gr-markdown,
|
| 147 |
+
.gradio-container .gr-markdown *,
|
| 148 |
+
.gradio-container .message,
|
| 149 |
+
.gradio-container .message *,
|
| 150 |
+
.gradio-container .wrap,
|
| 151 |
+
.gradio-container .wrap * {
|
| 152 |
+
color: var(--ink) !important;
|
| 153 |
+
opacity: 1 !important;
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
.gradio-container .tab-nav button {
|
| 157 |
+
background: #f2f5ec !important;
|
| 158 |
+
border: 1px solid #c8d0bf !important;
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
.gradio-container .tab-nav button[aria-selected="true"] {
|
| 162 |
+
background: #dcebdc !important;
|
| 163 |
+
border: 1px solid #7cad93 !important;
|
| 164 |
+
font-weight: 700 !important;
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
.hero {
|
| 168 |
border: 1px solid var(--line);
|
| 169 |
background: linear-gradient(120deg, #fffdf5 0%, #f4fffa 100%);
|
|
|
|
| 461 |
return code, f"Room created: {code}"
|
| 462 |
|
| 463 |
|
| 464 |
+
def start_free_play(team_name: str, budget_per_team: int = 20) -> Tuple[dict, str]:
|
| 465 |
+
team_name = (team_name or "").strip()
|
| 466 |
+
if not team_name:
|
| 467 |
+
return {}, "Provide a team name to start free play."
|
| 468 |
+
|
| 469 |
+
code = f"FREE-{random.randint(1000, 9999)}"
|
| 470 |
+
with rooms_lock:
|
| 471 |
+
while code in rooms:
|
| 472 |
+
code = f"FREE-{random.randint(1000, 9999)}"
|
| 473 |
+
rooms[code] = {
|
| 474 |
+
"created_at": _now_ms(),
|
| 475 |
+
"round": 1,
|
| 476 |
+
"budget_per_team": int(budget_per_team),
|
| 477 |
+
"active_event": None,
|
| 478 |
+
"teams": {
|
| 479 |
+
team_name: {
|
| 480 |
+
"joined_at": _now_ms(),
|
| 481 |
+
"budget_left": int(budget_per_team),
|
| 482 |
+
"unlocked_datasets": set(),
|
| 483 |
+
"profit_points": 0,
|
| 484 |
+
"customer_points": 0,
|
| 485 |
+
"submissions": [],
|
| 486 |
+
}
|
| 487 |
+
},
|
| 488 |
+
"log": [{"t": _now_ms(), "type": "free_play_start", "team": team_name}],
|
| 489 |
+
}
|
| 490 |
+
return {"room": code, "team": team_name}, f"Free play started in {code} as {team_name}."
|
| 491 |
+
|
| 492 |
+
|
| 493 |
def join_room(room_code: str, team_name: str) -> Tuple[dict, str]:
|
| 494 |
room_code = room_code.strip().upper()
|
| 495 |
team_name = team_name.strip()
|
|
|
|
| 912 |
<h1>AI Café Challenge</h1>
|
| 913 |
<p>Run your campus café like a data team: unlock evidence, test decisions, and iterate with tutor feedback.</p>
|
| 914 |
</div>
|
| 915 |
+
"""
|
| 916 |
+
)
|
| 917 |
+
gr.Markdown(
|
| 918 |
+
"""
|
| 919 |
+
### How to play
|
| 920 |
+
1. Join an existing room, or click **Start Free Play** to explore solo.
|
| 921 |
+
2. Ask the assistant for hints and request datasets with a clear business question.
|
| 922 |
+
3. Use unlocked files and plots to build evidence.
|
| 923 |
+
4. Submit a decision with model card + decision report.
|
| 924 |
+
5. Use **Tutor Feedback (Step-by-step)** to improve your work each round.
|
| 925 |
"""
|
| 926 |
)
|
| 927 |
|
|
|
|
| 932 |
room_in = gr.Textbox(label="Room code", placeholder="ROOM-1234")
|
| 933 |
team_in = gr.Textbox(label="Team name", placeholder="e.g., Team Orion")
|
| 934 |
join_btn = gr.Button("Join")
|
| 935 |
+
free_play_btn = gr.Button("Start Free Play", variant="primary")
|
| 936 |
|
| 937 |
join_status = gr.Markdown("Not joined.", elem_classes=["block-panel"])
|
| 938 |
header = gr.Markdown("")
|
|
|
|
| 995 |
sess, msg = join_room(room_code, team_name)
|
| 996 |
return sess, msg
|
| 997 |
|
| 998 |
+
def _free_play(team_name):
|
| 999 |
+
return start_free_play(team_name)
|
| 1000 |
+
|
| 1001 |
join_btn.click(_join, [room_in, team_in], [session, join_status]).then(
|
| 1002 |
ui_refresh, [session], [header, score_table, event_text]
|
| 1003 |
)
|
| 1004 |
+
free_play_btn.click(_free_play, [team_in], [session, join_status]).then(
|
| 1005 |
+
ui_refresh, [session], [header, score_table, event_text]
|
| 1006 |
+
)
|
| 1007 |
|
| 1008 |
refresh_btn.click(ui_refresh, [session], [header, score_table, event_text])
|
| 1009 |
|