Deevyankar commited on
Commit
ac75d2d
·
verified ·
1 Parent(s): 09d1bbf

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +46 -18
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py (FULL REPLACEMENT - session_state upload fix + grade>=C pass logic)
2
  import streamlit as st
3
  import pandas as pd
4
  import numpy as np
@@ -14,21 +14,24 @@ st.caption(
14
  )
15
 
16
  # -----------------------------
17
- # Grade logic (FINAL as per user)
18
  # -----------------------------
19
  def grade_pass_fail(g):
20
  if pd.isna(g):
21
  return "Unknown"
22
  g = str(g).strip().upper()
23
 
 
24
  if g.startswith(("D", "E", "F")):
25
  return "Fail"
26
 
 
27
  if g.startswith("C"):
28
  if g == "C-" or g.startswith("C-"):
29
  return "Fail"
30
  return "Pass"
31
 
 
32
  if g.startswith(("A", "B")):
33
  return "Pass"
34
 
@@ -54,6 +57,7 @@ def coerce_numeric(df: pd.DataFrame, cols):
54
 
55
 
56
  def detect_student_rows(df: pd.DataFrame, grade_col: str) -> pd.DataFrame:
 
57
  tmp_grade = df[grade_col].astype(str).str.strip()
58
  grade_like = tmp_grade.str.match(r"^[A-Fa-f][\+\-]?$", na=False)
59
 
@@ -87,6 +91,7 @@ def infer_component_cols(df: pd.DataFrame, grade_col: str, sno_col: str) -> list
87
  component_cols = [c for c in df.columns if c in common and c not in [grade_col, sno_col]]
88
 
89
  if not component_cols:
 
90
  numeric_cols = []
91
  for c in df.columns:
92
  if c in [grade_col, sno_col]:
@@ -106,14 +111,20 @@ def infer_component_cols(df: pd.DataFrame, grade_col: str, sno_col: str) -> list
106
 
107
  def add_consistency(df: pd.DataFrame, component_cols: list[str]) -> pd.DataFrame:
108
  df = df.copy()
109
- cols_for_sd = [c for c in component_cols if c.lower() != "total" and c in df.columns and pd.api.types.is_numeric_dtype(df[c])]
 
 
 
110
  df["Consistency_SD"] = df[cols_for_sd].std(axis=1, skipna=True) if len(cols_for_sd) >= 2 else np.nan
111
  return df
112
 
113
 
114
  def make_fail_reason_hints(df: pd.DataFrame, component_cols: list[str]) -> pd.DataFrame:
115
  df = df.copy()
116
- comps = [c for c in component_cols if c.lower() != "total" and c in df.columns and pd.api.types.is_numeric_dtype(df[c])]
 
 
 
117
 
118
  if not comps:
119
  df["FailReasonHint"] = np.where(df["PassFail"] == "Fail", "Grade below C.", "")
@@ -146,7 +157,7 @@ def make_fail_reason_hints(df: pd.DataFrame, component_cols: list[str]) -> pd.Da
146
 
147
 
148
  # -----------------------------
149
- # Upload (HF safe using session_state)
150
  # -----------------------------
151
  uploaded = st.file_uploader("Upload Excel (.xlsx)", type=["xlsx"], key="uploader")
152
 
@@ -154,32 +165,39 @@ if "file_bytes" not in st.session_state:
154
  st.session_state["file_bytes"] = None
155
  if "file_name" not in st.session_state:
156
  st.session_state["file_name"] = None
 
 
157
 
158
- # If new upload exists, store it in session_state (survives reruns)
159
  if uploaded is not None:
160
  st.session_state["file_bytes"] = uploaded.getvalue()
161
  st.session_state["file_name"] = uploaded.name
 
162
 
163
- # If still no bytes, stop safely
164
  if st.session_state["file_bytes"] is None:
165
  st.info("Upload an Excel file to begin.")
166
  st.stop()
167
 
168
- file_bytes = st.session_state["file_bytes"]
169
- bio = io.BytesIO(file_bytes)
170
-
171
- try:
172
- xls = pd.ExcelFile(bio)
173
- except Exception as e:
174
- st.error(f"Could not read Excel file. Error: {e}")
175
- st.stop()
 
176
 
177
- sheet = st.selectbox("Select sheet", xls.sheet_names, index=0)
178
 
179
- bio.seek(0)
 
180
  raw = pd.read_excel(bio, sheet_name=sheet)
181
  raw = normalize_headers(raw)
182
 
 
 
 
183
  grade_col_name = pick_grade_column(raw)
184
 
185
  df = detect_student_rows(raw, grade_col_name)
@@ -343,13 +361,23 @@ def student_drilldown_view(d: pd.DataFrame):
343
  with c3:
344
  st.metric("At Risk", "Yes" if row.get("Fail") else "No")
345
 
 
 
 
 
346
 
347
  def export_view(d: pd.DataFrame):
348
  st.subheader("Export for Power BI")
349
  clean_csv = d.to_csv(index=False).encode("utf-8")
350
- st.download_button("⬇️ Download Cleaned Data (CSV)", clean_csv, file_name="cleaned_marks_with_passfail.csv", mime="text/csv")
 
 
 
 
 
351
 
352
 
 
353
  if view == "Executive (Management)":
354
  executive_view(filtered)
355
  elif view == "Risk & Intervention":
 
1
+ # app.py (UPDATED FULL FILE - HF/Streamlit stable uploader + no xls NameError + Grade>=C pass)
2
  import streamlit as st
3
  import pandas as pd
4
  import numpy as np
 
14
  )
15
 
16
  # -----------------------------
17
+ # Grade logic (as specified)
18
  # -----------------------------
19
  def grade_pass_fail(g):
20
  if pd.isna(g):
21
  return "Unknown"
22
  g = str(g).strip().upper()
23
 
24
+ # Explicit FAIL
25
  if g.startswith(("D", "E", "F")):
26
  return "Fail"
27
 
28
+ # C- is FAIL, all other C variants are PASS
29
  if g.startswith("C"):
30
  if g == "C-" or g.startswith("C-"):
31
  return "Fail"
32
  return "Pass"
33
 
34
+ # A/B (with any +/-) are PASS
35
  if g.startswith(("A", "B")):
36
  return "Pass"
37
 
 
57
 
58
 
59
  def detect_student_rows(df: pd.DataFrame, grade_col: str) -> pd.DataFrame:
60
+ # keep rows that look like grade (A, B+, C-, etc.) OR have any numeric mark
61
  tmp_grade = df[grade_col].astype(str).str.strip()
62
  grade_like = tmp_grade.str.match(r"^[A-Fa-f][\+\-]?$", na=False)
63
 
 
91
  component_cols = [c for c in df.columns if c in common and c not in [grade_col, sno_col]]
92
 
93
  if not component_cols:
94
+ # fallback: numeric columns except grade/sno
95
  numeric_cols = []
96
  for c in df.columns:
97
  if c in [grade_col, sno_col]:
 
111
 
112
  def add_consistency(df: pd.DataFrame, component_cols: list[str]) -> pd.DataFrame:
113
  df = df.copy()
114
+ cols_for_sd = [
115
+ c for c in component_cols
116
+ if c.lower() != "total" and c in df.columns and pd.api.types.is_numeric_dtype(df[c])
117
+ ]
118
  df["Consistency_SD"] = df[cols_for_sd].std(axis=1, skipna=True) if len(cols_for_sd) >= 2 else np.nan
119
  return df
120
 
121
 
122
  def make_fail_reason_hints(df: pd.DataFrame, component_cols: list[str]) -> pd.DataFrame:
123
  df = df.copy()
124
+ comps = [
125
+ c for c in component_cols
126
+ if c.lower() != "total" and c in df.columns and pd.api.types.is_numeric_dtype(df[c])
127
+ ]
128
 
129
  if not comps:
130
  df["FailReasonHint"] = np.where(df["PassFail"] == "Fail", "Grade below C.", "")
 
157
 
158
 
159
  # -----------------------------
160
+ # Upload (HF stable using session_state)
161
  # -----------------------------
162
  uploaded = st.file_uploader("Upload Excel (.xlsx)", type=["xlsx"], key="uploader")
163
 
 
165
  st.session_state["file_bytes"] = None
166
  if "file_name" not in st.session_state:
167
  st.session_state["file_name"] = None
168
+ if "sheet_names" not in st.session_state:
169
+ st.session_state["sheet_names"] = None
170
 
171
+ # Persist uploaded bytes across reruns
172
  if uploaded is not None:
173
  st.session_state["file_bytes"] = uploaded.getvalue()
174
  st.session_state["file_name"] = uploaded.name
175
+ st.session_state["sheet_names"] = None # reset sheets on new upload
176
 
 
177
  if st.session_state["file_bytes"] is None:
178
  st.info("Upload an Excel file to begin.")
179
  st.stop()
180
 
181
+ # Load sheet names into session_state (prevents xls NameError on reruns)
182
+ if st.session_state["sheet_names"] is None:
183
+ try:
184
+ bio = io.BytesIO(st.session_state["file_bytes"])
185
+ xls_local = pd.ExcelFile(bio)
186
+ st.session_state["sheet_names"] = xls_local.sheet_names
187
+ except Exception as e:
188
+ st.error(f"Could not read Excel file: {e}")
189
+ st.stop()
190
 
191
+ sheet = st.selectbox("Select sheet", st.session_state["sheet_names"], index=0)
192
 
193
+ # Read selected sheet
194
+ bio = io.BytesIO(st.session_state["file_bytes"])
195
  raw = pd.read_excel(bio, sheet_name=sheet)
196
  raw = normalize_headers(raw)
197
 
198
+ # -----------------------------
199
+ # Build dataframe
200
+ # -----------------------------
201
  grade_col_name = pick_grade_column(raw)
202
 
203
  df = detect_student_rows(raw, grade_col_name)
 
361
  with c3:
362
  st.metric("At Risk", "Yes" if row.get("Fail") else "No")
363
 
364
+ hint = row.get("FailReasonHint", "")
365
+ if hint:
366
+ st.write("**Reason hint:**", hint)
367
+
368
 
369
  def export_view(d: pd.DataFrame):
370
  st.subheader("Export for Power BI")
371
  clean_csv = d.to_csv(index=False).encode("utf-8")
372
+ st.download_button(
373
+ "⬇️ Download Cleaned Data (CSV)",
374
+ clean_csv,
375
+ file_name="cleaned_marks_with_passfail.csv",
376
+ mime="text/csv"
377
+ )
378
 
379
 
380
+ # Render view
381
  if view == "Executive (Management)":
382
  executive_view(filtered)
383
  elif view == "Risk & Intervention":