lynn-twinkl commited on
Commit
f2a00b6
·
1 Parent(s): 78c2168

Added tabs to keep interface cleaner

Browse files
Files changed (1) hide show
  1. app.py +64 -92
app.py CHANGED
@@ -5,6 +5,7 @@
5
  import streamlit as st
6
  import pandas as pd
7
  from io import BytesIO
 
8
 
9
  # ---- FUNCTIONS ----
10
 
@@ -46,105 +47,76 @@ if uploaded_file is not None:
46
  # Read raw bytes for caching and repeated use --> this ensure all the processing isn't repeated when a user changes the filters
47
  raw = uploaded_file.read()
48
 
49
- with st.expander("Data Preview", icon='📊'):
50
- st.markdown("Here's the data you uploaded")
51
- df_orig = pd.read_csv(BytesIO(raw))
52
- st.dataframe(df_orig)
53
-
54
  ## ---- PROCESSED DATA (CACHED) ----
55
 
56
  df, freeform_col = load_and_process(raw)
57
 
58
  ## ---- INTERACTIVE FILTERING & REVIEW INTERFACE ----
59
 
60
- st.sidebar.title("Filters")
61
- min_idx = float(df['necessity_index'].min())
62
- max_idx = float(df['necessity_index'].max())
63
- filter_range = st.sidebar.slider(
64
- "Necessity Index Range", min_value=min_idx, max_value=max_idx, value=(min_idx, max_idx)
65
- )
66
- filtered_df = df[df['necessity_index'].between(filter_range[0], filter_range[1])]
67
-
68
- # Sidebar summary
69
- st.sidebar.markdown(f"**Total Applications:** {len(df)}")
70
- st.sidebar.markdown(f"**Filtered Applications:** {len(filtered_df)}")
71
-
72
- ## ---- NECESSITY INDEX CHART ----
73
-
74
- st.header("Processed Applications")
75
- st.markdown("#### Necessity Index Distribution")
76
-
77
- with st.expander("Learn about our indexing algorithm", icon='🌱'):
78
- st.markdown(
79
- """
80
- This algorithm is designed to help us evaluate grant applications by assigning a "necessity score" to each application. Here's how it works:
81
-
82
- #### Lexicon
83
-
84
- **Keywords:** The algorithm looks for specific keywords in the application text. These keywords are grouped into five main categories:
85
- - **Urgency:** Words like "urgent" and "immediate" that express pressing needs.
86
- - **Severity:** Words like "severe" and "desperate" that indicate serious issues.
87
- - **Vulnerability:** Words like "disability" "SEND", "underserved" that represent at-risk groups.
88
- - **Emotional Appeal:** Words like "hope", "committed", "passionate", and "love" that communicate an emotional connection to the mission of each application.
89
- - **Superlatives:** Words like "completely", "massive", and "significantly" that can serve as proxies for passionate writing.
90
-
91
- #### Weights
92
-
93
- Each category is **assigned a weight accroding to its importance** with **urgency and vulnerability markers** having the highest weights, followed by **severity markers**, and lastly, **superlatives and emotional appeal markers**.
94
-
95
- #### Scoring Process
96
-
97
- - **Need Identification:** The algorithm scans through the application text to count
98
- how many times each keyword appears.
99
- - **Weighted Scoring:** It multiplies these counts by the corresponding category weight to compute a total "necessity index", and then normalises the values to produce a range between 0 and 1.
100
-
101
- #### What It Means
102
-
103
- Together, all these scores help can help us rank the applications based on how critical the prize is to each school, with a **higher necessity index suggesting a more compelling case** and a lower score suggesting a "want" rather than a "need".
104
- """
 
 
 
105
  )
106
 
107
- st.bar_chart(df['necessity_index'])
108
-
109
- # Review applications
110
- st.subheader("Filtered Applications")
111
- st.markdown("To filter applications, use the app's side panel on the left-hand side.")
112
- for idx, row in filtered_df.iterrows():
113
- with st.expander(f"Application \#{idx} | Necessity: {row['necessity_index']:.1f}"):
114
- col1, col2 = st.columns((1, 3))
115
- col1.metric("Necessity", f"{row['necessity_index']:.1f}")
116
- col1.metric("Urgency", f"{row['urgency_score']}")
117
- col1.metric("Severity", f"{row['severity_score']}")
118
- col1.metric("Vulnerability", f"{row['vulnerability_score']}")
119
- # Clean usage items
120
- usage_items = [item for item in row['Usage'] if item and item.lower() != 'none']
121
- col2.markdown("**EXCERPT**")
122
- col2.write(row[freeform_col])
123
- if usage_items:
124
- col2.markdown("**EXTRACTED USAGE ITEMS:**")
125
- # Display usage items as colored pills
126
- pills_html = "".join(
127
- f"<span style='display:inline-block;background-color:#D7E0FB;color:#3C63C9;border-radius:20px;padding:4px 10px;margin:2px;'>{item}</span>"
128
- for item in usage_items
129
- )
130
- col2.markdown(pills_html, unsafe_allow_html=True)
131
- else:
132
- col2.markdown("*No specific usage items extracted.*")
133
- # Shortlist checkbox
134
- st.checkbox(
135
- "Shortlist this application",
136
- key=f"shortlist_{idx}"
137
  )
138
 
139
- # Shortlist summary and download
140
- shortlisted = [
141
- i for i in filtered_df.index
142
- if st.session_state.get(f"shortlist_{i}", False)
143
- ]
144
- st.sidebar.markdown(f"**Shortlisted:** {len(shortlisted)}")
145
- if shortlisted:
146
- csv = df.loc[shortlisted].to_csv(index=False).encode('utf-8')
147
- st.sidebar.download_button(
148
- "Download Shortlist", csv, "shortlist.csv", "text/csv"
149
- )
150
-
 
5
  import streamlit as st
6
  import pandas as pd
7
  from io import BytesIO
8
+ from streamlit_extras.metric_cards import style_metric_cards
9
 
10
  # ---- FUNCTIONS ----
11
 
 
47
  # Read raw bytes for caching and repeated use --> this ensure all the processing isn't repeated when a user changes the filters
48
  raw = uploaded_file.read()
49
 
 
 
 
 
 
50
  ## ---- PROCESSED DATA (CACHED) ----
51
 
52
  df, freeform_col = load_and_process(raw)
53
 
54
  ## ---- INTERACTIVE FILTERING & REVIEW INTERFACE ----
55
 
56
+ tab1, tab2 = st.tabs(["Shortlist Manager","Insights"])
57
+
58
+ with tab1:
59
+ st.sidebar.title("Filters")
60
+ min_idx = float(df['necessity_index'].min())
61
+ max_idx = float(df['necessity_index'].max())
62
+ filter_range = st.sidebar.slider(
63
+ "Necessity Index Range", min_value=min_idx, max_value=max_idx, value=(min_idx, max_idx)
64
+ )
65
+ filtered_df = df[df['necessity_index'].between(filter_range[0], filter_range[1])]
66
+
67
+ # Sidebar summary
68
+ st.sidebar.markdown(f"**Total Applications:** {len(df)}")
69
+ st.sidebar.markdown(f"**Filtered Applications:** {len(filtered_df)}")
70
+
71
+ ## ---- NECESSITY INDEX CHART ----
72
+
73
+ # Review applications
74
+ st.subheader("Filtered Applications")
75
+ st.markdown("To filter applications, use the app's side panel on the left-hand side.")
76
+ for idx, row in filtered_df.iterrows():
77
+ with st.expander(f"Application \#{idx}"):
78
+ st.write("")
79
+ col1, col2, col3, col4 = st.columns(4)
80
+ col1.metric("Necessity", f"{row['necessity_index']:.1f}")
81
+ col2.metric("Urgency", f"{int(row['urgency_score'])}")
82
+ col3.metric("Severity", f"{int(row['severity_score'])}")
83
+ col4.metric("Vulnerability", f"{int(row['vulnerability_score'])}")
84
+ style_metric_cards(box_shadow=False, border_left_color='#E7F4FF',background_color='#E7F4FF', border_size_px=0, border_radius_px=6)
85
+ # Clean usage items
86
+ usage_items = [item for item in row['Usage'] if item and item.lower() != 'none']
87
+ st.markdown("##### Excerpt")
88
+ st.write(row[freeform_col])
89
+ if usage_items:
90
+ st.markdown("##### Usage")
91
+ # Display usage items as colored pills
92
+ pills_html = "".join(
93
+ 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>"
94
+ for item in usage_items
95
+ )
96
+ st.markdown(pills_html, unsafe_allow_html=True)
97
+ else:
98
+ st.caption("*No usage found*")
99
+ st.write("")
100
+ # Shortlist checkbox
101
+ st.checkbox(
102
+ "Add to shortlist",
103
+ key=f"shortlist_{idx}"
104
  )
105
 
106
+ # Shortlist summary and download
107
+ shortlisted = [
108
+ i for i in filtered_df.index
109
+ if st.session_state.get(f"shortlist_{i}", False)
110
+ ]
111
+ st.sidebar.markdown(f"**Shortlisted:** {len(shortlisted)}")
112
+ if shortlisted:
113
+ csv = df.loc[shortlisted].to_csv(index=False).encode('utf-8')
114
+ st.sidebar.download_button(
115
+ "Download Shortlist", csv, "shortlist.csv", "text/csv"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  )
117
 
118
+ with tab2:
119
+ st.subheader("Necessity Index Distribution")
120
+ st.write("")
121
+ st.bar_chart(df['necessity_index'], color='#81bc4d')
122
+ st.dataframe(df, hide_index=True)