Spaces:
Sleeping
Sleeping
File size: 12,199 Bytes
cbc02eb eca077f cbc02eb |
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 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
# app.py
import streamlit as st
import numpy as np
import matplotlib.pyplot as plt
import random
st.set_page_config(page_title="Cell–Cell Communication Builder", layout="wide")
# -----------------------------
# Base option sets (your terms)
# -----------------------------
SECRETING_CELLS_BASE = [
"— select —",
"Presynaptic neuron",
"Hypothalamus/Pituitary/Adrenal cortex",
"Cardiomyocyte",
"Tumor cell",
]
MOLECULES_BASE = [
"— select —",
"EGF",
"Neurotransmitter",
"Intracellular ions",
"Cortisol",
]
RECEIVING_CELLS_BASE = [
"— select —",
"Neighboring cardiomyocytes",
"Same cell",
"Postsynaptic cell",
"Liver & skeletal muscle",
]
SIGNAL_TYPES_BASE = [
"— select —",
"direct",
"autocrine",
"paracrine",
"endocrine",
]
MOLECULE_CLASS_BASE = [
"— select —",
"hydrophilic",
"lipophilic",
"N/A (not applicable)",
]
RECEPTOR_LOCS_BASE = [
"— select —",
"membrane-bound",
"intracellular",
"N/A (not applicable)",
]
# -----------------------------
# Scenarios (titles simplified; type NOT shown in title)
# -----------------------------
SCENARIOS = {
"Cardiomyocyte signaling": {
"secreting": "Cardiomyocyte",
"molecule": "Intracellular ions",
"receiving": "Neighboring cardiomyocytes",
"type": "direct",
"mol_class": "N/A (not applicable)",
"receptor": "N/A (not applicable)",
"explain": {
"type": "Gap junctions are contact dependent → direct signaling.",
"mol_class": "Electrical/ionic coupling across connexons; not a classic ligand.",
"receptor": "No classic receptor: current spreads via channels/pores.",
},
},
"Cancer proliferation": {
"secreting": "Tumor cell",
"molecule": "EGF",
"receiving": "Same cell",
"type": "autocrine",
"mol_class": "hydrophilic",
"receptor": "membrane-bound",
"explain": {
"type": "Cell releases a signal that acts on itself → autocrine.",
"mol_class": "EGF is a protein → hydrophilic → can’t cross the bilayer.",
"receptor": "EGF binds EGFR (RTK) at the membrane.",
},
},
"Synapse signaling": {
"secreting": "Presynaptic neuron",
"molecule": "Neurotransmitter",
"receiving": "Postsynaptic cell",
"type": "paracrine",
"mol_class": "hydrophilic",
"receptor": "membrane-bound",
"explain": {
"type": "Very short-distance diffusion across synaptic cleft → paracrine.",
"mol_class": "Classical neurotransmitters act extracellularly.",
"receptor": "Postsynaptic receptors are membrane proteins (ionotropic/GPCR).",
},
},
"HPA axis": {
"secreting": "Hypothalamus/Pituitary/Adrenal cortex",
"molecule": "Cortisol",
"receiving": "Liver & skeletal muscle",
"type": "endocrine",
"mol_class": "lipophilic",
"receptor": "intracellular",
"explain": {
"type": "Hormone travels via blood to distant targets → endocrine.",
"mol_class": "Cortisol is steroidal → lipophilic → crosses the membrane.",
"receptor": "Steroids bind cytosolic/nuclear receptors → gene transcription.",
},
},
}
# -----------------------------
# Session state helpers
# -----------------------------
def shuffled(opts):
head, rest = opts[0], opts[1:]
random.shuffle(rest)
return [head] + rest
def ensure_state():
if "secret_opts" not in st.session_state:
st.session_state.secret_opts = shuffled(SECRETING_CELLS_BASE[:])
st.session_state.molecule_opts = shuffled(MOLECULES_BASE[:])
st.session_state.recv_opts = shuffled(RECEIVING_CELLS_BASE[:])
st.session_state.type_opts = shuffled(SIGNAL_TYPES_BASE[:])
st.session_state.class_opts = shuffled(MOLECULE_CLASS_BASE[:])
st.session_state.recept_opts = shuffled(RECEPTOR_LOCS_BASE[:])
if "selections" not in st.session_state:
st.session_state.selections = {
"secreting": "— select —",
"molecule": "— select —",
"receiving": "— select —",
"type": "— select —",
"mol_class": "— select —",
"receptor": "— select —",
}
def reshuffle_all():
st.session_state.secret_opts = shuffled(SECRETING_CELLS_BASE[:])
st.session_state.molecule_opts = shuffled(MOLECULES_BASE[:])
st.session_state.recv_opts = shuffled(RECEIVING_CELLS_BASE[:])
st.session_state.type_opts = shuffled(SIGNAL_TYPES_BASE[:])
st.session_state.class_opts = shuffled(MOLECULE_CLASS_BASE[:])
st.session_state.recept_opts = shuffled(RECEPTOR_LOCS_BASE[:])
clear_selections()
def clear_selections():
for k in st.session_state.selections.keys():
st.session_state.selections[k] = "— select —"
def evaluate(sel, key, scenario_key):
if sel == "— select —":
return None, "Incomplete — choose an option."
correct = SCENARIOS[scenario_key][key]
if sel == correct:
return True, "✔"
explain_map = SCENARIOS[scenario_key]["explain"]
why = explain_map.get("type" if key not in ("mol_class", "receptor") else key, "")
return False, f"Expected: {correct}. {why}"
def draw_diagram(selections, results, scenario_label):
fig, ax = plt.subplots(figsize=(9, 4.6))
ax.set_xlim(0, 13)
ax.set_ylim(0, 6.3)
ax.axis("off")
def color_box(x, y, text, ok, blank=False):
w, h = 2.8, 1.0
face = "#FFF8E1" if blank else ("#E8F5E9" if ok else "#FDECEA")
edge = "#FFB300" if blank else ("#2E7D32" if ok else "#C62828")
ax.add_patch(plt.Rectangle((x, y), w, h, fc=face, ec=edge, lw=2))
ax.text(x + w/2, y + h/2, text, ha="center", va="center", fontsize=11)
return (x, y, w, h)
def arrow(start_rect, end_rect, label=""):
x, y, w, h = start_rect
x2, y2, w2, h2 = end_rect
ax.annotate("", xy=(x2, y2 + h/2), xytext=(x + w, y + h/2),
arrowprops=dict(arrowstyle="->", lw=2))
if label:
ax.text((x + w + x2)/2, y + h/2 + 0.15, label, ha="center", fontsize=10)
# top row: secreting → molecule → receiving
s_ok = results["secreting"][0] if results["secreting"][0] is not None else True
m_ok = results["molecule"][0] if results["molecule"][0] is not None else True
r_ok = results["receiving"][0] if results["receiving"][0] is not None else True
s_rect = color_box(0.6, 3.9, f"Secreting Cell/Tissue\n{selections['secreting']}", s_ok, selections['secreting']=="— select —")
m_rect = color_box(4.6, 3.9, f"Molecule\n{selections['molecule']}", m_ok, selections['molecule']=="— select —")
r_rect = color_box(8.6, 3.9, f"Receiving Cell/Tissue\n{selections['receiving']}", r_ok, selections['receiving']=="— select —")
# bottom row: type → class → receptor
t_ok = results["type"][0] if results["type"][0] is not None else True
c_ok = results["mol_class"][0] if results["mol_class"][0] is not None else True
rc_ok = results["receptor"][0] if results["receptor"][0] is not None else True
t_rect = color_box(2.6, 1.6, f"Signaling Type\n{selections['type']}", t_ok, selections['type']=="— select —")
c_rect = color_box(6.6, 1.6, f"Molecule Class\n{selections['mol_class']}", c_ok, selections['mol_class']=="— select —")
rc_rect = color_box(10.6,1.6, f"Receptor Location\n{selections['receptor']}", rc_ok, selections['receptor']=="— select —")
arrow(s_rect, m_rect, "signal")
arrow(m_rect, r_rect, "response")
arrow(t_rect, c_rect)
arrow(c_rect, rc_rect)
ax.text(6.5, 5.9, scenario_label, ha="center", fontsize=12, fontweight="bold")
st.pyplot(fig)
# -----------------------------
# App UI
# -----------------------------
ensure_state()
st.title("Cell–Cell Communication Builder")
left, right = st.columns([1.1, 0.9])
with left:
scenario_label = st.selectbox(
"Scenario",
options=list(SCENARIOS.keys()),
index=0,
key="scenario_select"
)
with right:
shuffle_clicked = st.button("🔀 Shuffle options", use_container_width=True)
if shuffle_clicked:
reshuffle_all()
st.markdown("Build a consistent map of **Secreting cell/tissue → Molecule → Receiving cell/tissue** and choose **Signaling type**, **Molecule class**, and **Receptor location**. Then click **Test**.")
col1, col2 = st.columns(2)
with col1:
st.subheader("Actors")
st.session_state.selections["secreting"] = st.selectbox(
"Secreting Cell or Tissue",
options=st.session_state.secret_opts,
index=st.session_state.secret_opts.index(st.session_state.selections["secreting"])
if st.session_state.selections["secreting"] in st.session_state.secret_opts else 0,
key="sec_dd",
)
st.session_state.selections["molecule"] = st.selectbox(
"Molecule",
options=st.session_state.molecule_opts,
index=st.session_state.molecule_opts.index(st.session_state.selections["molecule"])
if st.session_state.selections["molecule"] in st.session_state.molecule_opts else 0,
key="mol_dd",
)
st.session_state.selections["receiving"] = st.selectbox(
"Receiving Cell or Tissue",
options=st.session_state.recv_opts,
index=st.session_state.recv_opts.index(st.session_state.selections["receiving"])
if st.session_state.selections["receiving"] in st.session_state.recv_opts else 0,
key="recv_dd",
)
with col2:
st.subheader("Mechanism")
st.session_state.selections["type"] = st.selectbox(
"Signaling Type",
options=st.session_state.type_opts,
index=st.session_state.type_opts.index(st.session_state.selections["type"])
if st.session_state.selections["type"] in st.session_state.type_opts else 0,
key="type_dd",
)
st.session_state.selections["mol_class"] = st.selectbox(
"Molecule Class",
options=st.session_state.class_opts,
index=st.session_state.class_opts.index(st.session_state.selections["mol_class"])
if st.session_state.selections["mol_class"] in st.session_state.class_opts else 0,
key="class_dd",
)
st.session_state.selections["receptor"] = st.selectbox(
"Receptor Location",
options=st.session_state.recept_opts,
index=st.session_state.recept_opts.index(st.session_state.selections["receptor"])
if st.session_state.selections["receptor"] in st.session_state.recept_opts else 0,
key="recept_dd",
)
action_col1, action_col2 = st.columns([1,1])
with action_col1:
tested = st.button("✅ Test", type="primary", use_container_width=True)
with action_col2:
cleared = st.button("🧹 Clear / Reshuffle", use_container_width=True)
if cleared:
reshuffle_all()
results = {}
if tested:
keys = ["secreting", "molecule", "receiving", "type", "mol_class", "receptor"]
for k in keys:
results[k] = evaluate(st.session_state.selections[k], k, st.session_state.scenario_select)
incomplete = any(v[0] is None for v in results.values())
n_ok = sum(1 for v in results.values() if v[0] is True)
total = len(results)
if incomplete:
st.warning("◻️ **INCOMPLETE** — make all selections to test consistency.")
elif n_ok == total:
st.success(f"✅ **CONSISTENT** ({n_ok}/{total}) — Nice! Logical mapping.")
else:
st.error(f"⚠️ **INCONSISTENT** ({n_ok}/{total}). Try again—no hints provided.")
draw_diagram(st.session_state.selections, results, st.session_state.scenario_select)
else:
tmp = {k:(None,"") for k in ["secreting","molecule","receiving","type","mol_class","receptor"]}
draw_diagram(st.session_state.selections, tmp, st.session_state.scenario_select)
st.caption("Options are shuffled on load and when you clear/reshuffle, to emphasize reasoning over pattern matching.")
|