soysouce commited on
Commit
04c1883
·
verified ·
1 Parent(s): 2f8e97d

Upload sem_project.py

Browse files
Files changed (1) hide show
  1. sem_project.py +197 -0
sem_project.py ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sqlite3
2
+ from datetime import datetime
3
+ from pathlib import Path
4
+ import pandas as pd
5
+ import streamlit as st
6
+
7
+ # -----------------------------
8
+ # Config & constants
9
+ # -----------------------------
10
+ st.set_page_config(page_title="Topic Interest Survey", page_icon="🗳️", layout="centered")
11
+
12
+ DB_PATH = Path("interests.db")
13
+ TABLE = "responses"
14
+ HEADERS = ["timestamp", "name", "topics"]
15
+
16
+ TOPICS = [
17
+ "Hamid — Mapping the Innovation Ecosystem of a Tech Sector",
18
+ "Hamid — Public Narratives of Corporate AI Adoption",
19
+ "Milad — Geography of Goods & Services (EUIPO trademarks)",
20
+ "Milad — Cross‑Border Relations — Nordic Startup Hyperlinks",
21
+ "Milad — AI Champions in the Periphery (Company websites)",
22
+ "Milad — Diffusion of ISO 27001 (Company websites)",
23
+ "Milad — Goods & Services Classification (Large corpora)",
24
+ "Roman — Automation & Quality Evaluation (n8n logs)",
25
+ "Roman — AI & Labour Markets (O*NET)",
26
+ "Roman — Long‑Form Document Structuring",
27
+ "Richard & Roman/Milad — Knowledge Graph Construction from Text",
28
+ "Richard & Roman/Milad Optimising LLM Generation Settings",
29
+ ]
30
+
31
+ # -----------------------------
32
+ # DB helpers
33
+ # -----------------------------
34
+ @st.cache_resource(show_spinner=False)
35
+ def get_conn():
36
+ conn = sqlite3.connect(DB_PATH, check_same_thread=False)
37
+ conn.execute("PRAGMA journal_mode=WAL;")
38
+ conn.execute(
39
+ f"""
40
+ CREATE TABLE IF NOT EXISTS {TABLE} (
41
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
42
+ timestamp TEXT NOT NULL,
43
+ name TEXT NOT NULL,
44
+ topics TEXT NOT NULL
45
+ )
46
+ """
47
+ )
48
+ conn.commit()
49
+ return conn
50
+
51
+
52
+ def insert_row(conn, name: str, topics: list[str]):
53
+ ts = datetime.now().isoformat(timespec="seconds")
54
+ topics_str = ", ".join(topics)
55
+ conn.execute(
56
+ f"INSERT INTO {TABLE} (timestamp, name, topics) VALUES (?, ?, ?)",
57
+ (ts, name.strip(), topics_str),
58
+ )
59
+ conn.commit()
60
+
61
+
62
+ def delete_row(conn, row_id: int):
63
+ conn.execute(f"DELETE FROM {TABLE} WHERE id = ?", (row_id,))
64
+ conn.commit()
65
+
66
+
67
+ def load_df(conn) -> pd.DataFrame:
68
+ df = pd.read_sql_query(
69
+ f"SELECT id, timestamp, name, topics FROM {TABLE} ORDER BY timestamp DESC", conn
70
+ )
71
+ if not df.empty:
72
+ df["topic_list"] = df["topics"].str.split(", ")
73
+ return df
74
+
75
+ # -----------------------------
76
+ # UI helpers
77
+ # -----------------------------
78
+
79
+ def clear_form_state():
80
+ st.session_state.pop("name", None)
81
+ st.session_state.pop("choices", None)
82
+
83
+
84
+ # -----------------------------
85
+ # App
86
+ # -----------------------------
87
+ conn = get_conn()
88
+
89
+ st.title("🗳️ Topic Interest Survey")
90
+ st.caption("Pick up to two topics, add your name, and see the public roster of interests.")
91
+
92
+ survey_tab, roster_tab = st.tabs(["Survey", "Roster"])
93
+
94
+ with survey_tab:
95
+ with st.form("survey_form", clear_on_submit=False):
96
+ name = st.text_input("Your name", key="name", placeholder="e.g., Chenghao Luo")
97
+ choices = st.multiselect(
98
+ "Choose topics (max 2)",
99
+ options=TOPICS,
100
+ key="choices",
101
+ help="You can only pick at most two.",
102
+ )
103
+ submitted = st.form_submit_button("Submit interest")
104
+
105
+ if submitted:
106
+ # Validation
107
+ if not name or not name.strip():
108
+ st.error("Please enter your name.")
109
+ elif len(choices) == 0 or len(choices) > 2:
110
+ st.error("Please choose 1 or 2 topics.")
111
+ else:
112
+ try:
113
+ insert_row(conn, name, choices)
114
+ st.success("Thanks! Your interest was recorded.")
115
+ clear_form_state()
116
+ except sqlite3.IntegrityError as e:
117
+ st.error(f"Duplicate? Could not save: {e}")
118
+ except Exception as e:
119
+ st.error(f"Unexpected error: {e}")
120
+
121
+ with roster_tab:
122
+ df = load_df(conn)
123
+ if df is None or df.empty:
124
+ st.info("No entries yet.")
125
+ else:
126
+ # Filters
127
+ col1, col2 = st.columns([2, 1])
128
+ with col1:
129
+ name_filter = st.text_input("Filter by name", placeholder="type part of a name…")
130
+ with col2:
131
+ topic_filter = st.selectbox("Filter by topic", options=["(All)"] + TOPICS)
132
+
133
+ df_view = df.copy()
134
+ if name_filter:
135
+ df_view = df_view[df_view["name"].str.contains(name_filter, case=False, na=False)]
136
+ if topic_filter and topic_filter != "(All)":
137
+ df_view = df_view[df_view["topics"].str.contains(topic_filter, na=False)]
138
+
139
+ st.subheader("Roster")
140
+ st.dataframe(
141
+ df_view[["timestamp", "name", "topics"]].rename(
142
+ columns={"timestamp": "Timestamp", "name": "Name", "topics": "Topics"}
143
+ ),
144
+ use_container_width=True,
145
+ hide_index=True,
146
+ )
147
+
148
+ # Delete section
149
+ st.divider()
150
+ st.subheader("Delete a record")
151
+ st.caption("Select a specific submission to delete. This cannot be undone.")
152
+ # Build nice labels for selection
153
+ options = [
154
+ (
155
+ int(r.id),
156
+ f"#{int(r.id)} — {r.name} — {r.topics} — {r.timestamp}"
157
+ )
158
+ for _, r in df.iterrows()
159
+ ]
160
+ if options:
161
+ selected_label = st.selectbox(
162
+ "Choose a record",
163
+ options=[lbl for _, lbl in options],
164
+ index=0,
165
+ key="delete_select",
166
+ )
167
+ # Map back to id
168
+ label_to_id = {lbl: rid for rid, lbl in options}
169
+ col_del, col_warn = st.columns([1, 3])
170
+ with col_del:
171
+ if st.button("🗑️ Delete selected", type="primary"):
172
+ delete_row(conn, label_to_id[selected_label])
173
+ st.success("Record deleted. Refresh the tab to see updates.")
174
+ with col_warn:
175
+ st.info("Tip: Use filters above to narrow the list, then delete.")
176
+ else:
177
+ st.caption("No records to delete.")
178
+
179
+ # Topic counts
180
+ st.subheader("Topic counts")
181
+ counts = (
182
+ df.explode("topic_list")["topic_list"].value_counts().rename_axis("Topic").reset_index(name="Count")
183
+ )
184
+ st.bar_chart(counts.set_index("Topic"))
185
+
186
+ # Download CSV
187
+ csv = df[["timestamp", "name", "topics"]].to_csv(index=False).encode("utf-8")
188
+ st.download_button(
189
+ "⬇️ Download CSV",
190
+ data=csv,
191
+ file_name="topic_interests.csv",
192
+ mime="text/csv",
193
+ )
194
+
195
+ # Footer
196
+ st.write("")
197
+ st.caption("Built with Streamlit + SQLite. Data stored locally in interests.db.")