Spaces:
Sleeping
Sleeping
File size: 6,902 Bytes
04c1883 | 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 | import sqlite3
from datetime import datetime
from pathlib import Path
import pandas as pd
import streamlit as st
# -----------------------------
# Config & constants
# -----------------------------
st.set_page_config(page_title="Topic Interest Survey", page_icon="🗳️", layout="centered")
DB_PATH = Path("interests.db")
TABLE = "responses"
HEADERS = ["timestamp", "name", "topics"]
TOPICS = [
"Hamid — Mapping the Innovation Ecosystem of a Tech Sector",
"Hamid — Public Narratives of Corporate AI Adoption",
"Milad — Geography of Goods & Services (EUIPO trademarks)",
"Milad — Cross‑Border Relations — Nordic Startup Hyperlinks",
"Milad — AI Champions in the Periphery (Company websites)",
"Milad — Diffusion of ISO 27001 (Company websites)",
"Milad — Goods & Services Classification (Large corpora)",
"Roman — Automation & Quality Evaluation (n8n logs)",
"Roman — AI & Labour Markets (O*NET)",
"Roman — Long‑Form Document Structuring",
"Richard & Roman/Milad — Knowledge Graph Construction from Text",
"Richard & Roman/Milad Optimising LLM Generation Settings",
]
# -----------------------------
# DB helpers
# -----------------------------
@st.cache_resource(show_spinner=False)
def get_conn():
conn = sqlite3.connect(DB_PATH, check_same_thread=False)
conn.execute("PRAGMA journal_mode=WAL;")
conn.execute(
f"""
CREATE TABLE IF NOT EXISTS {TABLE} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT NOT NULL,
name TEXT NOT NULL,
topics TEXT NOT NULL
)
"""
)
conn.commit()
return conn
def insert_row(conn, name: str, topics: list[str]):
ts = datetime.now().isoformat(timespec="seconds")
topics_str = ", ".join(topics)
conn.execute(
f"INSERT INTO {TABLE} (timestamp, name, topics) VALUES (?, ?, ?)",
(ts, name.strip(), topics_str),
)
conn.commit()
def delete_row(conn, row_id: int):
conn.execute(f"DELETE FROM {TABLE} WHERE id = ?", (row_id,))
conn.commit()
def load_df(conn) -> pd.DataFrame:
df = pd.read_sql_query(
f"SELECT id, timestamp, name, topics FROM {TABLE} ORDER BY timestamp DESC", conn
)
if not df.empty:
df["topic_list"] = df["topics"].str.split(", ")
return df
# -----------------------------
# UI helpers
# -----------------------------
def clear_form_state():
st.session_state.pop("name", None)
st.session_state.pop("choices", None)
# -----------------------------
# App
# -----------------------------
conn = get_conn()
st.title("🗳️ Topic Interest Survey")
st.caption("Pick up to two topics, add your name, and see the public roster of interests.")
survey_tab, roster_tab = st.tabs(["Survey", "Roster"])
with survey_tab:
with st.form("survey_form", clear_on_submit=False):
name = st.text_input("Your name", key="name", placeholder="e.g., Chenghao Luo")
choices = st.multiselect(
"Choose topics (max 2)",
options=TOPICS,
key="choices",
help="You can only pick at most two.",
)
submitted = st.form_submit_button("Submit interest")
if submitted:
# Validation
if not name or not name.strip():
st.error("Please enter your name.")
elif len(choices) == 0 or len(choices) > 2:
st.error("Please choose 1 or 2 topics.")
else:
try:
insert_row(conn, name, choices)
st.success("Thanks! Your interest was recorded.")
clear_form_state()
except sqlite3.IntegrityError as e:
st.error(f"Duplicate? Could not save: {e}")
except Exception as e:
st.error(f"Unexpected error: {e}")
with roster_tab:
df = load_df(conn)
if df is None or df.empty:
st.info("No entries yet.")
else:
# Filters
col1, col2 = st.columns([2, 1])
with col1:
name_filter = st.text_input("Filter by name", placeholder="type part of a name…")
with col2:
topic_filter = st.selectbox("Filter by topic", options=["(All)"] + TOPICS)
df_view = df.copy()
if name_filter:
df_view = df_view[df_view["name"].str.contains(name_filter, case=False, na=False)]
if topic_filter and topic_filter != "(All)":
df_view = df_view[df_view["topics"].str.contains(topic_filter, na=False)]
st.subheader("Roster")
st.dataframe(
df_view[["timestamp", "name", "topics"]].rename(
columns={"timestamp": "Timestamp", "name": "Name", "topics": "Topics"}
),
use_container_width=True,
hide_index=True,
)
# Delete section
st.divider()
st.subheader("Delete a record")
st.caption("Select a specific submission to delete. This cannot be undone.")
# Build nice labels for selection
options = [
(
int(r.id),
f"#{int(r.id)} — {r.name} — {r.topics} — {r.timestamp}"
)
for _, r in df.iterrows()
]
if options:
selected_label = st.selectbox(
"Choose a record",
options=[lbl for _, lbl in options],
index=0,
key="delete_select",
)
# Map back to id
label_to_id = {lbl: rid for rid, lbl in options}
col_del, col_warn = st.columns([1, 3])
with col_del:
if st.button("🗑️ Delete selected", type="primary"):
delete_row(conn, label_to_id[selected_label])
st.success("Record deleted. Refresh the tab to see updates.")
with col_warn:
st.info("Tip: Use filters above to narrow the list, then delete.")
else:
st.caption("No records to delete.")
# Topic counts
st.subheader("Topic counts")
counts = (
df.explode("topic_list")["topic_list"].value_counts().rename_axis("Topic").reset_index(name="Count")
)
st.bar_chart(counts.set_index("Topic"))
# Download CSV
csv = df[["timestamp", "name", "topics"]].to_csv(index=False).encode("utf-8")
st.download_button(
"⬇️ Download CSV",
data=csv,
file_name="topic_interests.csv",
mime="text/csv",
)
# Footer
st.write("")
st.caption("Built with Streamlit + SQLite. Data stored locally in interests.db.")
|