Tesneem commited on
Commit
af06a87
Β·
verified Β·
1 Parent(s): 5eb6a1c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +28 -34
app.py CHANGED
@@ -1,6 +1,5 @@
1
  # app.py β€” Streamlit radar charts from MongoDB (scores 0–1)
2
  import os
3
- import json
4
  from datetime import date
5
  from typing import Dict, List
6
 
@@ -52,24 +51,13 @@ def safe_mean(vals):
52
  vals = [v for v in vals if v is not None]
53
  return float(np.mean(vals)) if vals else 0.0
54
 
55
-
56
  def to_frame(records: List[dict]) -> pd.DataFrame:
57
- if not records:
58
- return pd.DataFrame()
59
- df = pd.DataFrame(records)
60
- # Expand skills into columns in SKILLS order
61
- skill_df = pd.json_normalize(df.get("skills", {})).reindex(columns=SKILLS)
62
- for k in SKILLS:
63
- if k not in skill_df:
64
- skill_df[k] = 0.0
65
- df = pd.concat([df.drop(columns=["skills"], errors="ignore"), skill_df], axis=1)
66
- return df
67
-
68
 
69
  def aggregate_groups_row(row: pd.Series) -> Dict[str, float]:
70
  return {g: safe_mean([float(row.get(s, 0.0)) for s in members]) for g, members in SKILL_GROUPS.items()}
71
 
72
-
73
  def summarize(records: List[dict], level: str = "student") -> pd.DataFrame:
74
  """Average per label over SKILLS; level in {student, student+source}."""
75
  df = to_frame(records)
@@ -79,8 +67,11 @@ def summarize(records: List[dict], level: str = "student") -> pd.DataFrame:
79
  df["label"] = df["student"].astype(str) + " β€” " + df["source"].astype(str)
80
  else:
81
  df["label"] = df["student"].astype(str)
82
- return df.groupby("label")[SKILLS].mean().reset_index()
83
-
 
 
 
84
 
85
  def plot_radar(df: pd.DataFrame, grouped: bool, title: str):
86
  if df.empty:
@@ -123,10 +114,8 @@ def plot_radar(df: pd.DataFrame, grouped: bool, title: str):
123
  )
124
  return fig
125
 
126
-
127
  # ------------------- Mongo Access (secrets-only) -------------------
128
  def _get_secret(name: str) -> str | None:
129
- # Prefer Streamlit secrets, fallback to env vars
130
  try:
131
  val = st.secrets.get(name)
132
  if val is not None:
@@ -135,7 +124,6 @@ def _get_secret(name: str) -> str | None:
135
  pass
136
  return os.getenv(name)
137
 
138
-
139
  def _build_uri(db_name: str | None) -> str | None:
140
  user = _get_secret("MONGO_USER")
141
  pw = _get_secret("MONGO_PASS")
@@ -145,19 +133,15 @@ def _build_uri(db_name: str | None) -> str | None:
145
  user_q = quote_plus(user)
146
  pw_q = quote_plus(pw)
147
  db_path = f"/{db_name}" if db_name else ""
148
- # TLS flags help on some hosts; SRV requires dnspython (present on Streamlit Cloud)
149
  return (
150
  f"mongodb+srv://{user_q}:{pw_q}@{cluster}{db_path}"
151
  f"?retryWrites=true&w=majority&tls=true&tlsAllowInvalidCertificates=true"
152
  )
153
 
154
-
155
  @st.cache_resource(show_spinner=False)
156
  def _client(uri: str):
157
- # Use a cached client resource (not cache_data) for connections
158
  return MongoClient(uri, serverSelectionTimeoutMS=10000)
159
 
160
-
161
  @st.cache_data(show_spinner=False)
162
  def mongo_distinct(uri: str, db: str, coll: str, field: str) -> List[str]:
163
  if not uri:
@@ -169,7 +153,6 @@ def mongo_distinct(uri: str, db: str, coll: str, field: str) -> List[str]:
169
  except Exception:
170
  return []
171
 
172
-
173
  @st.cache_data(show_spinner=False)
174
  def mongo_records(
175
  uri: str,
@@ -180,6 +163,7 @@ def mongo_records(
180
  start: str | None,
181
  end: str | None,
182
  ) -> List[dict]:
 
183
  if not uri:
184
  return []
185
  q = {}
@@ -196,25 +180,33 @@ def mongo_records(
196
  try:
197
  c = _client(uri)
198
  proj = {"_id": 0, "student": 1, "source": 1, "date": 1, "skills": 1}
199
- out = list(c[db][coll].find(q, proj))
200
- # normalize scores to floats; default 0.0
201
- for r in out:
202
- r.setdefault("skills", {})
203
- r["skills"] = {k: float(r["skills"].get(k, 0.0)) for k in SKILLS}
204
- return out
 
 
 
 
 
 
 
 
 
 
205
  except Exception:
206
  return []
207
 
208
-
209
  # ------------------- UI -------------------
210
- st.title("πŸ“Š Student Skill Radar")
211
 
212
  with st.sidebar:
213
  st.subheader("MongoDB Settings")
214
  db_name = st.text_input("Database name", value="student_skills")
215
  coll_name = st.text_input("Collection name", value="responses_IFE_2025")
216
 
217
- # Build from MONGO_USER / MONGO_PASS / MONGO_CLUSTER only
218
  mongo_uri = _build_uri(db_name)
219
 
220
  if not mongo_uri:
@@ -261,9 +253,11 @@ with right:
261
  if df.empty:
262
  st.info("No data. Adjust filters or check Mongo connection.")
263
  else:
264
- st.dataframe(df, use_container_width=True, height=450)
265
  csv = df.to_csv(index=False).encode("utf-8")
266
  st.download_button("Download CSV", data=csv, file_name="skill_scores.csv", mime="text/csv")
 
 
267
 
268
 
269
  # # app.py
 
1
  # app.py β€” Streamlit radar charts from MongoDB (scores 0–1)
2
  import os
 
3
  from datetime import date
4
  from typing import Dict, List
5
 
 
51
  vals = [v for v in vals if v is not None]
52
  return float(np.mean(vals)) if vals else 0.0
53
 
 
54
  def to_frame(records: List[dict]) -> pd.DataFrame:
55
+ # Records are returned already flattened with skill columns
56
+ return pd.DataFrame(records) if records else pd.DataFrame()
 
 
 
 
 
 
 
 
 
57
 
58
  def aggregate_groups_row(row: pd.Series) -> Dict[str, float]:
59
  return {g: safe_mean([float(row.get(s, 0.0)) for s in members]) for g, members in SKILL_GROUPS.items()}
60
 
 
61
  def summarize(records: List[dict], level: str = "student") -> pd.DataFrame:
62
  """Average per label over SKILLS; level in {student, student+source}."""
63
  df = to_frame(records)
 
67
  df["label"] = df["student"].astype(str) + " β€” " + df["source"].astype(str)
68
  else:
69
  df["label"] = df["student"].astype(str)
70
+ # ensure missing skill columns exist (safety)
71
+ for k in SKILLS:
72
+ if k not in df:
73
+ df[k] = 0.0
74
+ return df.groupby("label", dropna=False)[SKILLS].mean(numeric_only=True).reset_index()
75
 
76
  def plot_radar(df: pd.DataFrame, grouped: bool, title: str):
77
  if df.empty:
 
114
  )
115
  return fig
116
 
 
117
  # ------------------- Mongo Access (secrets-only) -------------------
118
  def _get_secret(name: str) -> str | None:
 
119
  try:
120
  val = st.secrets.get(name)
121
  if val is not None:
 
124
  pass
125
  return os.getenv(name)
126
 
 
127
  def _build_uri(db_name: str | None) -> str | None:
128
  user = _get_secret("MONGO_USER")
129
  pw = _get_secret("MONGO_PASS")
 
133
  user_q = quote_plus(user)
134
  pw_q = quote_plus(pw)
135
  db_path = f"/{db_name}" if db_name else ""
 
136
  return (
137
  f"mongodb+srv://{user_q}:{pw_q}@{cluster}{db_path}"
138
  f"?retryWrites=true&w=majority&tls=true&tlsAllowInvalidCertificates=true"
139
  )
140
 
 
141
  @st.cache_resource(show_spinner=False)
142
  def _client(uri: str):
 
143
  return MongoClient(uri, serverSelectionTimeoutMS=10000)
144
 
 
145
  @st.cache_data(show_spinner=False)
146
  def mongo_distinct(uri: str, db: str, coll: str, field: str) -> List[str]:
147
  if not uri:
 
153
  except Exception:
154
  return []
155
 
 
156
  @st.cache_data(show_spinner=False)
157
  def mongo_records(
158
  uri: str,
 
163
  start: str | None,
164
  end: str | None,
165
  ) -> List[dict]:
166
+ """Return FLAT rows: student, source, date, and one column per skill (0–1 floats)."""
167
  if not uri:
168
  return []
169
  q = {}
 
180
  try:
181
  c = _client(uri)
182
  proj = {"_id": 0, "student": 1, "source": 1, "date": 1, "skills": 1}
183
+ docs = list(c[db][coll].find(q, proj))
184
+ rows = []
185
+ for d in docs:
186
+ base = {
187
+ "student": str(d.get("student", "")),
188
+ "source": str(d.get("source", "")),
189
+ "date": str(d.get("date", "")),
190
+ }
191
+ sd = d.get("skills") or {}
192
+ for k in SKILLS:
193
+ try:
194
+ base[k] = float(sd.get(k, 0.0))
195
+ except Exception:
196
+ base[k] = 0.0
197
+ rows.append(base)
198
+ return rows
199
  except Exception:
200
  return []
201
 
 
202
  # ------------------- UI -------------------
203
+ st.title("πŸ“Š Student Skill Radar β€” MongoDB (secrets-based)")
204
 
205
  with st.sidebar:
206
  st.subheader("MongoDB Settings")
207
  db_name = st.text_input("Database name", value="student_skills")
208
  coll_name = st.text_input("Collection name", value="responses_IFE_2025")
209
 
 
210
  mongo_uri = _build_uri(db_name)
211
 
212
  if not mongo_uri:
 
253
  if df.empty:
254
  st.info("No data. Adjust filters or check Mongo connection.")
255
  else:
256
+ # Removed the table per your request
257
  csv = df.to_csv(index=False).encode("utf-8")
258
  st.download_button("Download CSV", data=csv, file_name="skill_scores.csv", mime="text/csv")
259
+ st.caption(f"{len(df)} line(s) aggregated.")
260
+
261
 
262
 
263
  # # app.py