lanna_lalala;- commited on
Commit
e7300cd
·
1 Parent(s): 1e64800
Files changed (2) hide show
  1. phase/Teacher_view/classmanage.py +4 -5
  2. utils/api.py +76 -7
phase/Teacher_view/classmanage.py CHANGED
@@ -53,16 +53,15 @@ def show_page():
53
  if not name:
54
  st.error("Enter a real name, not whitespace.")
55
  else:
56
- # positional args first, then keywords (we only use positional here)
57
  out = _prefer_db(
58
  "create_class",
59
  lambda tid, n: api.create_class(tid, n),
60
  None,
61
- teacher_id,
62
- name,
63
  )
64
  if out:
65
- st.session_state.selected_class_id = out.get("class_id")
66
  st.success(f'Classroom "{name}" created with code: {out.get("code","—")}')
67
  st.rerun()
68
  else:
@@ -85,7 +84,7 @@ def show_page():
85
  options = {f"{c.get('name','(unnamed)')} (Code: {c.get('code','')})": c for c in classes}
86
  selected_label = st.selectbox("Select a classroom", list(options.keys()))
87
  selected = options[selected_label]
88
- class_id = selected["class_id"]
89
 
90
  st.markdown("---")
91
  st.header(selected.get("name", "Classroom"))
 
53
  if not name:
54
  st.error("Enter a real name, not whitespace.")
55
  else:
 
56
  out = _prefer_db(
57
  "create_class",
58
  lambda tid, n: api.create_class(tid, n),
59
  None,
60
+ teacher_id, # positional arg
61
+ name, # positional arg
62
  )
63
  if out:
64
+ st.session_state.selected_class_id = out.get("class_id") or out.get("id")
65
  st.success(f'Classroom "{name}" created with code: {out.get("code","—")}')
66
  st.rerun()
67
  else:
 
84
  options = {f"{c.get('name','(unnamed)')} (Code: {c.get('code','')})": c for c in classes}
85
  selected_label = st.selectbox("Select a classroom", list(options.keys()))
86
  selected = options[selected_label]
87
+ class_id = selected.get("class_id") or selected.get("id")
88
 
89
  st.markdown("---")
90
  st.header(selected.get("name", "Classroom"))
utils/api.py CHANGED
@@ -88,6 +88,46 @@ def health():
88
 
89
  #---helpers
90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  #--helpers for student_db.py
92
  def user_stats(student_id: int):
93
  return _req("GET", f"/students/{student_id}/stats").json()
@@ -153,24 +193,53 @@ def student_assignments_for_class(student_id: int, class_id: int):
153
  return _json_or_raise(_req("GET", f"/classes/{class_id}/students/{student_id}/assignments"))
154
 
155
 
156
- # ---- Classes / Teacher endpoints ----
157
  def create_class(teacher_id: int, name: str):
158
- return _req("POST", f"/teachers/{teacher_id}/classes", json={"name": name}).json()
 
 
 
 
 
159
 
160
  def list_classes_by_teacher(teacher_id: int):
161
- return _req("GET", f"/teachers/{teacher_id}/classes").json()
 
 
 
 
 
 
 
162
 
163
  def list_students_in_class(class_id: int):
164
- return _req("GET", f"/classes/{class_id}/students").json()
 
 
 
 
165
 
166
  def class_content_counts(class_id: int):
167
- return _req("GET", f"/classes/{class_id}/content_counts").json()
 
 
 
 
 
168
 
169
  def list_class_assignments(class_id: int):
170
- return _req("GET", f"/classes/{class_id}/assignments").json()
 
 
 
 
171
 
172
  def class_analytics(class_id: int):
173
- return _req("GET", f"/classes/{class_id}/analytics").json()
 
 
 
 
174
 
175
  #--contentmanage.py helpers
176
 
 
88
 
89
  #---helpers
90
 
91
+ # --- Optional API prefix (e.g., "/api" or "/v1")
92
+ API_PREFIX_ENV = (os.getenv("BACKEND_API_PREFIX") or "").strip().rstrip("/")
93
+
94
+ def _prefixes():
95
+ # Try configured prefix first, then common fallbacks
96
+ seen, out = set(), []
97
+ for p in [API_PREFIX_ENV, "", "/api", "/v1", "/api/v1"]:
98
+ p = (p or "").strip()
99
+ p = "" if p == "" else ("/" + p.strip("/"))
100
+ if p not in seen:
101
+ out.append(p)
102
+ seen.add(p)
103
+ return out
104
+
105
+ def _try_candidates(method: str, candidates: list[tuple[str, dict]]):
106
+ """
107
+ candidates: list of (path, request_kwargs) where path starts with "/" and
108
+ kwargs may include {'params':..., 'json':...}.
109
+ Tries multiple prefixes (e.g., "", "/api", "/v1") and returns JSON for first 2xx.
110
+ Auth errors (401/403) are raised immediately.
111
+ """
112
+ tried = []
113
+ for pref in _prefixes():
114
+ for path, kw in candidates:
115
+ url = f"{BACKEND}{pref}{path}"
116
+ tried.append(f"{method} {url}")
117
+ try:
118
+ r = _session.request(method, url, timeout=DEFAULT_TIMEOUT, **kw)
119
+ except requests.RequestException as e:
120
+ # transient error: keep trying others
121
+ continue
122
+ if r.status_code in (401, 403):
123
+ snippet = (r.text or "")[:200]
124
+ raise RuntimeError(f"{method} {path} auth failed [{r.status_code}]: {snippet}")
125
+ if 200 <= r.status_code < 300:
126
+ return _json_or_raise(r)
127
+ # 404/405/etc.: try next candidate
128
+ raise RuntimeError("No matching endpoint for this operation. Tried:\n- " + "\n- ".join(tried))
129
+
130
+
131
  #--helpers for student_db.py
132
  def user_stats(student_id: int):
133
  return _req("GET", f"/students/{student_id}/stats").json()
 
193
  return _json_or_raise(_req("GET", f"/classes/{class_id}/students/{student_id}/assignments"))
194
 
195
 
196
+ # ---- Classes / Teacher endpoints (tolerant) ----
197
  def create_class(teacher_id: int, name: str):
198
+ return _try_candidates("POST", [
199
+ (f"/teachers/{teacher_id}/classes", {"json": {"name": name}}),
200
+ (f"/teachers/{teacher_id}/classrooms",{"json": {"name": name}}),
201
+ ("/classes", {"json": {"teacher_id": teacher_id, "name": name}}),
202
+ ("/classrooms", {"json": {"teacher_id": teacher_id, "name": name}}),
203
+ ])
204
 
205
  def list_classes_by_teacher(teacher_id: int):
206
+ return _try_candidates("GET", [
207
+ (f"/teachers/{teacher_id}/classes", {}),
208
+ (f"/teachers/{teacher_id}/classrooms", {}),
209
+ (f"/classes/by-teacher/{teacher_id}", {}),
210
+ (f"/classrooms/by-teacher/{teacher_id}", {}),
211
+ ("/classes", {"params": {"teacher_id": teacher_id}}),
212
+ ("/classrooms", {"params": {"teacher_id": teacher_id}}),
213
+ ])
214
 
215
  def list_students_in_class(class_id: int):
216
+ return _try_candidates("GET", [
217
+ (f"/classes/{class_id}/students", {}),
218
+ (f"/classrooms/{class_id}/students", {}),
219
+ ("/students", {"params": {"class_id": class_id}}),
220
+ ])
221
 
222
  def class_content_counts(class_id: int):
223
+ return _try_candidates("GET", [
224
+ (f"/classes/{class_id}/content_counts", {}),
225
+ (f"/classrooms/{class_id}/content_counts", {}),
226
+ (f"/classes/{class_id}/counts", {}),
227
+ (f"/classrooms/{class_id}/counts", {}),
228
+ ])
229
 
230
  def list_class_assignments(class_id: int):
231
+ return _try_candidates("GET", [
232
+ (f"/classes/{class_id}/assignments", {}),
233
+ (f"/classrooms/{class_id}/assignments", {}),
234
+ ("/assignments", {"params": {"class_id": class_id}}),
235
+ ])
236
 
237
  def class_analytics(class_id: int):
238
+ return _try_candidates("GET", [
239
+ (f"/classes/{class_id}/analytics", {}),
240
+ (f"/classrooms/{class_id}/analytics", {}),
241
+ ])
242
+
243
 
244
  #--contentmanage.py helpers
245