ShinyaJ commited on
Commit
a726ba3
·
verified ·
1 Parent(s): ee18bae

Upload 2 files

Browse files
Files changed (1) hide show
  1. app.py +31 -22
app.py CHANGED
@@ -3,6 +3,8 @@ import gradio as gr
3
  import pandas as pd
4
  import numpy as np
5
  import re
 
 
6
  from io import BytesIO
7
  from typing import List, Dict, Tuple, Optional
8
  try:
@@ -22,7 +24,7 @@ DESCRIPTION = """
22
  - หรือกรอก **หมายเลขคอลัมน์** ตามรายการ Available columns (เลขเริ่ม 1)
23
  5) Clean → เหลือเฉพาะ NAME, ID, และคอลัมน์วอร์ดที่เลือก (ค่าจัดอันดับถูกแปลงเป็นตัวเลข)
24
  6) Assign → สุ่มตามลำดับอันดับ โดยเคารพ capacity
25
- - **จะตรวจว่าจำนวนนักศึกษา <= ผลรวม capacity** (ขาดได้ แต่ห้ามเกิน)
26
  """
27
 
28
  WARD_CHOICES = [
@@ -41,8 +43,8 @@ AUTO_MAP = {
41
  "NAME": ["ชื่อ-สกุล", "ชื่อ - สกุล", "fullname", "full name", "name", "student name"],
42
  "ID": ["รหัสนักศึกษา", "รหัส", "student id", "id", "studentid"],
43
  "Medical": ["อายุรศาสตร์", "medical"],
44
- "Medical_1": ["อายุรศาสตร์_1", "medical_1", "med_1"],
45
- "Medical_2": ["อายุรศาสตร์_2", "medical_2", "med_2"],
46
  "Surgical": ["ศัลยศาสตร์", "surgical", "surgery"],
47
  "Pediatric": ["เด็ก", "pediatric", "pediatrics"],
48
  "Community": ["ชุมชน", "community"],
@@ -181,6 +183,7 @@ def build_cleaned_from_indices(df: pd.DataFrame,
181
 
182
  def random_assign(cleaned: pd.DataFrame,
183
  capacities: Dict[str, int]) -> Tuple[pd.DataFrame, pd.DataFrame, Dict[str, int]]:
 
184
  wards = [w for w in cleaned.columns if w not in ("NAME", "ID")]
185
  cap = {w: int(capacities.get(w, 0)) for w in wards}
186
 
@@ -218,6 +221,11 @@ def random_assign(cleaned: pd.DataFrame,
218
  not_assigned = result[result["AssignedWard"].isna()].copy()
219
  return result.fillna(""), not_assigned.fillna(""), cap
220
 
 
 
 
 
 
221
  # ===== Gradio callbacks =====
222
 
223
  def update_capacity_table(selected_wards: List[str]) -> pd.DataFrame:
@@ -230,6 +238,7 @@ def update_capacity_table(selected_wards: List[str]) -> pd.DataFrame:
230
  def on_upload(file, selected_wards):
231
  df, msg = read_table(file)
232
  if df is None:
 
233
  return gr.update(value=msg, visible=True), "", None, None, None, None, None, None, None, None, None, None
234
  # Show available columns
235
  avail = available_columns_text(df)
@@ -305,14 +314,15 @@ def on_clean(file, selected_wards, capacity_df, name_num, id_num,
305
  except Exception as e:
306
  return gr.update(value=f"❌ เกิดข้อผิดพลาด: {e}", visible=True), None, None, None
307
 
308
- buf = BytesIO()
309
- cleaned.to_csv(buf, index=False, encoding="utf-8-sig")
310
- buf.seek(0)
 
311
  info = "✓ Cleaning สำเร็จ"
312
- return gr.update(value=info, visible=True), cleaned.head(30), ("cleaned.csv", buf), len(cleaned)
313
 
314
  def on_assign(file, selected_wards, capacity_df, name_num, id_num,
315
- med_num, med1_num, med2_num, surg_num, ped_num, comm_num, psy_num, obs_num, seed):
316
  # Clean first to get the cleaned df and student count
317
  status, cleaned_preview, cleaned_file, n_students = on_clean(file, selected_wards, capacity_df, name_num, id_num,
318
  med_num, med1_num, med2_num, surg_num, ped_num, comm_num, psy_num, obs_num)
@@ -351,15 +361,18 @@ def on_assign(file, selected_wards, capacity_df, name_num, id_num,
351
  msg = f"❌ จำนวนผู้สมัคร {n_students} คน มากกว่า capacity รวม {total_capacity} ที่กำหนด (ขาดได้แต่ห้ามเกิน)"
352
  return gr.update(value=msg, visible=True), None, None, None, None
353
 
354
- assigned, not_assigned, leftover = random_assign(cleaned, cap_map, seed=int(seed) if str(seed).strip().isdigit() else None)
 
 
 
 
 
 
 
355
 
356
- out_all = BytesIO()
357
- assigned.to_csv(out_all, index=False, encoding="utf-8-sig"); out_all.seek(0)
358
- out_un = BytesIO()
359
- not_assigned.to_csv(out_un, index=False, encoding="utf-8-sig"); out_un.seek(0)
360
  leftover_text = "ความจุคงเหลือ:\n" + "\n".join([f"- {k}: {v}" for k, v in leftover.items()])
361
 
362
- return status, assigned.head(30), ("assigned.csv", out_all), ("not_assigned.csv", out_un), leftover_text
363
 
364
  with gr.Blocks(title=APP_TITLE) as demo:
365
  gr.Markdown(f"# {APP_TITLE}")
@@ -407,16 +420,12 @@ with gr.Blocks(title=APP_TITLE) as demo:
407
  psy_num = gr.Number(label="หมายเลขคอลัมน์ Psychiatric", precision=0)
408
  obs_num = gr.Number(label="หมายเลขคอลัมน์ Obstetrics", precision=0)
409
 
410
- auto_btn.click(
411
- fn=on_upload,
412
- inputs=[file, selected_wards],
413
- outputs=[status, available, name_num, id_num,
414
- med_num, med1_num, med2_num, surg_num, ped_num, comm_num, psy_num, obs_num]
415
- )
416
 
417
  with gr.Row():
418
  clean_btn = gr.Button("Clean data (ดูพรีวิว)", variant="primary")
419
- seed = gr.Textbox(label="Random seed (เว้นว่างเพื่อสุ่มใหม่)", value="")
420
 
421
  preview = gr.Dataframe(label="พรีวิวข้อมูลที่ผ่านการ clean (หัว 30 แถว)", visible=True)
422
  cleaned_file = gr.File(label="ดาวน์โหลดไฟล์ cleaned.csv")
@@ -437,7 +446,7 @@ with gr.Blocks(title=APP_TITLE) as demo:
437
  assign_btn.click(
438
  fn=on_assign,
439
  inputs=[file, selected_wards, capacity_df, name_num, id_num,
440
- med_num, med1_num, med2_num, surg_num, ped_num, comm_num, psy_num, obs_num, seed],
441
  outputs=[status, assigned_preview, assigned_file, not_assigned_file, leftover_text]
442
  )
443
 
 
3
  import pandas as pd
4
  import numpy as np
5
  import re
6
+ import os
7
+ import uuid
8
  from io import BytesIO
9
  from typing import List, Dict, Tuple, Optional
10
  try:
 
24
  - หรือกรอก **หมายเลขคอลัมน์** ตามรายการ Available columns (เลขเริ่ม 1)
25
  5) Clean → เหลือเฉพาะ NAME, ID, และคอลัมน์วอร์ดที่เลือก (ค่าจัดอันดับถูกแปลงเป็นตัวเลข)
26
  6) Assign → สุ่มตามลำดับอันดับ โดยเคารพ capacity
27
+ - **จะตรวจว่าจำนวนนักศึกษา <= ผลรวม capacity** (ขาดได้แต่ห้ามเกิน)
28
  """
29
 
30
  WARD_CHOICES = [
 
43
  "NAME": ["ชื่อ-สกุล", "ชื่อ - สกุล", "fullname", "full name", "name", "student name"],
44
  "ID": ["รหัสนักศึกษา", "รหัส", "student id", "id", "studentid"],
45
  "Medical": ["อายุรศาสตร์", "medical"],
46
+ "Medical_1": ["อายุรศาสตร์_1", "medical_1", "med_1","med1"],
47
+ "Medical_2": ["อายุรศาสตร์_2", "medical_2", "med_2", "med2"],
48
  "Surgical": ["ศัลยศาสตร์", "surgical", "surgery"],
49
  "Pediatric": ["เด็ก", "pediatric", "pediatrics"],
50
  "Community": ["ชุมชน", "community"],
 
183
 
184
  def random_assign(cleaned: pd.DataFrame,
185
  capacities: Dict[str, int]) -> Tuple[pd.DataFrame, pd.DataFrame, Dict[str, int]]:
186
+ """Assign by rank rounds; tie-break with numpy's global RNG (np.random.choice)."""
187
  wards = [w for w in cleaned.columns if w not in ("NAME", "ID")]
188
  cap = {w: int(capacities.get(w, 0)) for w in wards}
189
 
 
221
  not_assigned = result[result["AssignedWard"].isna()].copy()
222
  return result.fillna(""), not_assigned.fillna(""), cap
223
 
224
+ # ===== Helpers for temp file paths =====
225
+ def _tmp(name: str) -> str:
226
+ os.makedirs("/tmp", exist_ok=True)
227
+ return f"/tmp/{uuid.uuid4().hex}-{name}"
228
+
229
  # ===== Gradio callbacks =====
230
 
231
  def update_capacity_table(selected_wards: List[str]) -> pd.DataFrame:
 
238
  def on_upload(file, selected_wards):
239
  df, msg = read_table(file)
240
  if df is None:
241
+ # return flat outputs for all mapping fields
242
  return gr.update(value=msg, visible=True), "", None, None, None, None, None, None, None, None, None, None
243
  # Show available columns
244
  avail = available_columns_text(df)
 
314
  except Exception as e:
315
  return gr.update(value=f"❌ เกิดข้อผิดพลาด: {e}", visible=True), None, None, None
316
 
317
+ # Write to a unique temp file path
318
+ cleaned_path = _tmp("cleaned.csv")
319
+ cleaned.to_csv(cleaned_path, index=False, encoding="utf-8-sig")
320
+
321
  info = "✓ Cleaning สำเร็จ"
322
+ return gr.update(value=info, visible=True), cleaned.head(30), cleaned_path, len(cleaned)
323
 
324
  def on_assign(file, selected_wards, capacity_df, name_num, id_num,
325
+ med_num, med1_num, med2_num, surg_num, ped_num, comm_num, psy_num, obs_num):
326
  # Clean first to get the cleaned df and student count
327
  status, cleaned_preview, cleaned_file, n_students = on_clean(file, selected_wards, capacity_df, name_num, id_num,
328
  med_num, med1_num, med2_num, surg_num, ped_num, comm_num, psy_num, obs_num)
 
361
  msg = f"❌ จำนวนผู้สมัคร {n_students} คน มากกว่า capacity รวม {total_capacity} ที่กำหนด (ขาดได้แต่ห้ามเกิน)"
362
  return gr.update(value=msg, visible=True), None, None, None, None
363
 
364
+ # Assign without seed; use np.random.choice
365
+ assigned, not_assigned, leftover = random_assign(cleaned, cap_map)
366
+
367
+ # Write files to unique temp paths
368
+ assigned_path = _tmp("assigned.csv")
369
+ not_assigned_path = _tmp("not_assigned.csv")
370
+ assigned.to_csv(assigned_path, index=False, encoding="utf-8-sig")
371
+ not_assigned.to_csv(not_assigned_path, index=False, encoding="utf-8-sig")
372
 
 
 
 
 
373
  leftover_text = "ความจุคงเหลือ:\n" + "\n".join([f"- {k}: {v}" for k, v in leftover.items()])
374
 
375
+ return status, assigned.head(30), assigned_path, not_assigned_path, leftover_text
376
 
377
  with gr.Blocks(title=APP_TITLE) as demo:
378
  gr.Markdown(f"# {APP_TITLE}")
 
420
  psy_num = gr.Number(label="หมายเลขคอลัมน์ Psychiatric", precision=0)
421
  obs_num = gr.Number(label="หมายเลขคอลัมน์ Obstetrics", precision=0)
422
 
423
+ auto_btn.click(fn=on_upload, inputs=[file, selected_wards],
424
+ outputs=[status, available, name_num, id_num,
425
+ med_num, med1_num, med2_num, surg_num, ped_num, comm_num, psy_num, obs_num])
 
 
 
426
 
427
  with gr.Row():
428
  clean_btn = gr.Button("Clean data (ดูพรีวิว)", variant="primary")
 
429
 
430
  preview = gr.Dataframe(label="พรีวิวข้อมูลที่ผ่านการ clean (หัว 30 แถว)", visible=True)
431
  cleaned_file = gr.File(label="ดาวน์โหลดไฟล์ cleaned.csv")
 
446
  assign_btn.click(
447
  fn=on_assign,
448
  inputs=[file, selected_wards, capacity_df, name_num, id_num,
449
+ med_num, med1_num, med2_num, surg_num, ped_num, comm_num, psy_num, obs_num],
450
  outputs=[status, assigned_preview, assigned_file, not_assigned_file, leftover_text]
451
  )
452