lynn-twinkl commited on
Commit
29af03a
·
1 Parent(s): 6269878

UI Improvements

Browse files
Files changed (1) hide show
  1. app.py +69 -54
app.py CHANGED
@@ -13,6 +13,7 @@ from hdbscan import HDBSCAN
13
  import os
14
 
15
  from streamlit_extras.metric_cards import style_metric_cards
 
16
 
17
  # ---- FUNCTIONS ----
18
 
@@ -107,7 +108,8 @@ def run_topic_modeling():
107
  # MAIN APP SCRIPT
108
  ################################
109
 
110
- st.title("Community Collections Helper")
 
111
 
112
  uploaded_file = st.file_uploader("Upload grant applications file for analysis", type='csv')
113
 
@@ -122,7 +124,7 @@ if uploaded_file is not None:
122
  book_candidates_df = df[df['book_candidates'] == True]
123
 
124
  ###############################
125
- # SIDEBAR #
126
  ###############################
127
 
128
  with st.sidebar:
@@ -143,7 +145,7 @@ if uploaded_file is not None:
143
  st.title("Filters")
144
 
145
  ## --- Dataframe To Filter ---
146
- options = ['All applications', 'Not shortlisted']
147
  selected_view = st.pills('Choose data to filter', options, default='Not shortlisted')
148
  st.write("")
149
 
@@ -187,7 +189,7 @@ if uploaded_file is not None:
187
 
188
  ## =========== AUTOMATIC SHORTLIST =========
189
 
190
- st.header("Automatic Shortlist")
191
 
192
  csv_auto = auto_short_df.to_csv(index=False).encode("utf-8")
193
  all_processed_data = df.to_csv(index=False).encode("utf-8")
@@ -239,53 +241,59 @@ if uploaded_file is not None:
239
 
240
  ## ====== APPLICATIONS REVIEW =======
241
 
242
- st.divider()
243
- st.header("🌸 Manual Filtering")
244
- st.markdown(
245
- """
246
- Below you'll find applications that **did not** make it into the shortlist for you to manually review or append to the shortlist if desired.
247
 
248
- You may use the **side panel** filters to more easily sort through applications that you'd like to review.
249
- """
250
- )
251
-
252
- st.markdown("#### Filtered Applications")
253
  st.write("")
254
- for idx, row in filtered_df.iterrows():
255
- with st.expander(f"Application {row[id_col]}"):
256
- st.write("")
257
- col1, col2, col3, col4 = st.columns(4)
258
- col1.metric("Necessity", f"{row['necessity_index']:.1f}")
259
- col2.metric("Urgency", f"{int(row['urgency_score'])}")
260
- col3.metric("Severity", f"{int(row['severity_score'])}")
261
- col4.metric("Vulnerability", f"{int(row['vulnerability_score'])}")
262
-
263
- # HTML for clean usage items
264
- usage_items = [item for item in row['Usage'] if item and item.lower() != 'none']
265
- st.markdown("##### Excerpt")
266
- st.write(row[freeform_col])
267
- if usage_items:
268
- st.markdown("##### Usage")
269
- pills_html = "".join(
270
- f"<span style='display:inline-block;background-color:#E7F4FF;color:#125E9E;border-radius:20px;padding:4px 10px;margin:2px;font-size:0.95rem;'>{item}</span>"
271
- for item in usage_items
 
 
 
 
 
 
 
 
 
 
 
272
  )
273
- st.markdown(pills_html, unsafe_allow_html=True)
274
- else:
275
- st.caption("*No usage found*")
276
- st.write("")
277
-
278
- st.checkbox(
279
- "Add to shortlist",
280
- key=f"shortlist_{idx}"
281
- )
 
 
282
 
283
  # ======== SHORTLIST SUMMARY AND DOWNLOAD (MANUAL) ======
284
  shortlisted = [
285
  i for i in filtered_df.index
286
  if st.session_state.get(f"shortlist_{i}", False)
287
  ]
288
- st.sidebar.markdown(f"**Manual Shortlisted:** {len(shortlisted)}")
289
  if shortlisted:
290
  csv = df.loc[shortlisted].to_csv(index=False).encode('utf-8')
291
  st.sidebar.download_button(
@@ -293,30 +301,44 @@ if uploaded_file is not None:
293
  )
294
 
295
 
 
 
 
 
 
296
  #########################################
297
  # INSIGHTS TAB #
298
  #########################################
299
 
300
  with tab2:
301
 
 
302
  ## =========== DATA OVERVIEW ==========
303
- st.write("")
 
 
304
 
305
  col1, col2, col3 = st.columns(3)
306
  col1.metric("Avg. Word Count", f"{df['word_count'].mean().round(1)}")
307
  col2.metric("Median N.I", df['necessity_index'].median().round(2))
308
  col3.metric("Total Applications", len(df))
309
- st.html("<br>")
310
 
311
  ## --- NI Distribution Plot ---
312
- st.subheader("Necessity Index (NI) Distribution")
313
  ni_distribution_plt = plot_histogram(df, col_to_plot='necessity_index', bins=50)
314
  st.plotly_chart(ni_distribution_plt)
315
 
316
 
 
 
 
 
 
 
 
317
  # =========== TOPIC MODELING ============
318
 
319
- st.subheader("Topic Modeling")
 
320
 
321
  ## ------- 1. Tokenize texts into sentences -------
322
  nlp = topic_modeling_pipeline.load_spacy_model(model_name='en_core_web_sm')
@@ -353,12 +375,7 @@ if uploaded_file is not None:
353
  topics_df = topics_df[cols_to_move + [col for col in topics_df.columns if col not in cols_to_move]]
354
  topics_df.rename(columns={'CustomName':'Topic Name', 'Topic':'Topic Nr.'}, inplace=True)
355
 
356
- st.markdown("""
357
- ### Extracted Topics Table
358
- This table shows you the topics that have been extracted from the applications.
359
- """)
360
-
361
- with st.expander("How are topic extracted?", icon="❓", expanded=False):
362
 
363
  st.write("""
364
  **About Topic Modeling**
@@ -383,6 +400,4 @@ if uploaded_file is not None:
383
 
384
  st.plotly_chart(topic_count_plot, use_container_width=True)
385
 
386
-
387
-
388
 
 
13
  import os
14
 
15
  from streamlit_extras.metric_cards import style_metric_cards
16
+ from streamlit_extras.add_vertical_space import add_vertical_space
17
 
18
  # ---- FUNCTIONS ----
19
 
 
108
  # MAIN APP SCRIPT
109
  ################################
110
 
111
+ st.title("🪷 Community Collections Helper")
112
+ st.badge("Version 1.0.0", icon=':material/category:',color='violet')
113
 
114
  uploaded_file = st.file_uploader("Upload grant applications file for analysis", type='csv')
115
 
 
124
  book_candidates_df = df[df['book_candidates'] == True]
125
 
126
  ###############################
127
+ # SIDE PANNEL #
128
  ###############################
129
 
130
  with st.sidebar:
 
145
  st.title("Filters")
146
 
147
  ## --- Dataframe To Filter ---
148
+ options = ['All applications', 'Not shortlisted']
149
  selected_view = st.pills('Choose data to filter', options, default='Not shortlisted')
150
  st.write("")
151
 
 
189
 
190
  ## =========== AUTOMATIC SHORTLIST =========
191
 
192
+ st.header("Automatic Shortlist")
193
 
194
  csv_auto = auto_short_df.to_csv(index=False).encode("utf-8")
195
  all_processed_data = df.to_csv(index=False).encode("utf-8")
 
241
 
242
  ## ====== APPLICATIONS REVIEW =======
243
 
244
+ add_vertical_space(2)
245
+ st.header("Manual Filtering")
246
+ st.info("Use the **side panel** filters to more easily sort through applications that you'd like to review.", icon=':material/info:')
 
 
247
 
 
 
 
 
 
248
  st.write("")
249
+ if len(filtered_df) > 0:
250
+ st.markdown("#### Filtered Applications")
251
+ for idx, row in filtered_df.iterrows():
252
+ with st.expander(f"Application {row[id_col]}"):
253
+ st.write("")
254
+ col1, col2, col3, col4 = st.columns(4)
255
+ col1.metric("Necessity", f"{row['necessity_index']:.1f}")
256
+ col2.metric("Urgency", f"{int(row['urgency_score'])}")
257
+ col3.metric("Severity", f"{int(row['severity_score'])}")
258
+ col4.metric("Vulnerability", f"{int(row['vulnerability_score'])}")
259
+
260
+ # HTML for clean usage items
261
+ usage_items = [item for item in row['Usage'] if item and item.lower() != 'none']
262
+ st.markdown("##### Excerpt")
263
+ st.write(row[freeform_col])
264
+ if usage_items:
265
+ st.markdown("##### Usage")
266
+ pills_html = "".join(
267
+ f"<span style='display:inline-block;background-color:#E7F4FF;color:#125E9E;border-radius:20px;padding:4px 10px;margin:2px;font-size:0.95rem;'>{item}</span>"
268
+ for item in usage_items
269
+ )
270
+ st.markdown(pills_html, unsafe_allow_html=True)
271
+ else:
272
+ st.caption("*No usage found*")
273
+ st.write("")
274
+
275
+ st.checkbox(
276
+ "Add to shortlist",
277
+ key=f"shortlist_{idx}"
278
  )
279
+
280
+ else:
281
+ st.markdown(
282
+ """
283
+ <br>
284
+ <div style="text-align: center; font-size: 1.2em">
285
+ 🍂 <span style="color: grey;">No applications matched these filters...</span>
286
+ </div>
287
+ """,
288
+ unsafe_allow_html=True,
289
+ )
290
 
291
  # ======== SHORTLIST SUMMARY AND DOWNLOAD (MANUAL) ======
292
  shortlisted = [
293
  i for i in filtered_df.index
294
  if st.session_state.get(f"shortlist_{i}", False)
295
  ]
296
+ st.sidebar.markdown(f"**Manually Shortlisted:** {len(shortlisted)}")
297
  if shortlisted:
298
  csv = df.loc[shortlisted].to_csv(index=False).encode('utf-8')
299
  st.sidebar.download_button(
 
301
  )
302
 
303
 
304
+ add_vertical_space(5)
305
+ st.divider()
306
+ st.markdown(":grey[Made with 🩷 by the AI Innovation team &nbsp; | &nbsp; Contact: lynn.perez@twinkl.com]")
307
+
308
+
309
  #########################################
310
  # INSIGHTS TAB #
311
  #########################################
312
 
313
  with tab2:
314
 
315
+
316
  ## =========== DATA OVERVIEW ==========
317
+
318
+ st.header("General Insights")
319
+ add_vertical_space(1)
320
 
321
  col1, col2, col3 = st.columns(3)
322
  col1.metric("Avg. Word Count", f"{df['word_count'].mean().round(1)}")
323
  col2.metric("Median N.I", df['necessity_index'].median().round(2))
324
  col3.metric("Total Applications", len(df))
 
325
 
326
  ## --- NI Distribution Plot ---
 
327
  ni_distribution_plt = plot_histogram(df, col_to_plot='necessity_index', bins=50)
328
  st.plotly_chart(ni_distribution_plt)
329
 
330
 
331
+
332
+
333
+
334
+
335
+
336
+
337
+
338
  # =========== TOPIC MODELING ============
339
 
340
+ st.header("Topic Modeling")
341
+ add_vertical_space(1)
342
 
343
  ## ------- 1. Tokenize texts into sentences -------
344
  nlp = topic_modeling_pipeline.load_spacy_model(model_name='en_core_web_sm')
 
375
  topics_df = topics_df[cols_to_move + [col for col in topics_df.columns if col not in cols_to_move]]
376
  topics_df.rename(columns={'CustomName':'Topic Name', 'Topic':'Topic Nr.'}, inplace=True)
377
 
378
+ with st.expander("How are topic extracted?", icon="🌱", expanded=False):
 
 
 
 
 
379
 
380
  st.write("""
381
  **About Topic Modeling**
 
400
 
401
  st.plotly_chart(topic_count_plot, use_container_width=True)
402
 
 
 
403