Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| from collections import defaultdict | |
| import pandas as pd | |
| import os | |
| from datetime import datetime | |
| # ===================== | |
| # DISC Predictor | |
| # ===================== | |
| class DISCPredictor: | |
| def __init__(self, mapping=None): | |
| self.mapping = mapping if mapping else {} | |
| self.traits = ['D','I','S','C'] | |
| def predict_percentage(self, answers): | |
| scores = defaultdict(float) | |
| for q_idx, ans in enumerate(answers): | |
| key = q_idx + 1 | |
| ans = str(ans).upper().strip() | |
| qmap = self.mapping.get(key, {}) | |
| choice_map = qmap.get(ans, {}) | |
| for trait, w in choice_map.items(): | |
| scores[trait] += float(w) | |
| total = sum(scores.values()) if sum(scores.values()) != 0 else 1.0 | |
| percentages = {t: round((scores.get(t,0)/total)*100) for t in self.traits} | |
| max_score = max(scores.values()) if scores else 0 | |
| winners = [t for t, s in scores.items() if s == max_score] | |
| main_trait = '/'.join(sorted(winners)) if winners else "" | |
| return main_trait, percentages | |
| # ===================== | |
| # Mapping | |
| # ===================== | |
| mapping = { | |
| 1: {'A': {'D':2}, 'B': {'I':2}, 'C': {'S':2}, 'D': {'C':2}}, | |
| 2: {'A': {'C':2}, 'B': {'I':2}, 'C': {'S':2}, 'D': {'D':2}}, | |
| 3: {'A': {'S':2}, 'B': {'I':2}, 'C': {'C':2}, 'D': {'D':2}}, | |
| 4: {'A': {'S':2}, 'B': {'C':2}, 'C': {'D':2}, 'D': {'I':2}}, | |
| 5: {'A': {'D':2}, 'B': {'I':2}, 'C': {'S':2}, 'D': {'C':2}}, | |
| 6: {'A': {'I':2}, 'B': {'S':2}, 'C': {'C':2}, 'D': {'D':2}}, | |
| 7: {'A': {'S':2}, 'B': {'C':2}, 'C': {'I':2}, 'D': {'D':2}}, | |
| 8: {'A': {'C':2}, 'B': {'S':2}, 'C': {'D':2}, 'D': {'I':2}}, | |
| 9: {'A': {'D':2}, 'B': {'I':2}, 'C': {'S':2}, 'D': {'C':2}}, | |
| 10:{'A': {'I':2}, 'B': {'C':2}, 'C': {'D':2}, 'D': {'S':2}}, | |
| } | |
| # ===================== | |
| # คำถาม DISC | |
| # ===================== | |
| questions_text = { | |
| 1: {"ฉันรู้สึกว่ามันยากที่จะผ่อนคลาย ใช้ชีวิตสบาย ๆ หรืออยู่นิ่ง ๆ ": "A", | |
| "ฉันมีเพื่อนเยอะ ": "B", | |
| "ฉันพร้อมที่จะช่วยเหลือผู้อื่นเสมอ ": "C", | |
| "ฉันชอบความสมบูรณ์แบบ": "D"}, | |
| 2: {"ฉันเป็นคนเรียบร้อย มีระเบียบ": "A", | |
| "ฉันเป็นคนกระตือรือร้น ทั้ง work hard และ play hard": "B", | |
| "ฉันเป็นคนใจเย็นมาก": "C", | |
| "ฉันมีทางของตัวเอง": "D"}, | |
| 3: {"ฉันค่อนข้างพอใจกับวิธีชีวิตปกติของฉัน": "A", | |
| "ฉันมีความคิดสร้างสรรค์และไม่ชอบกฎเกณฑ์": "B", | |
| "ฉันชอบความเงียบและความสงบสุข": "C", | |
| "ฉันมองโลกในแง่ดี": "D"}, | |
| 4: {"ฉันเป็นผู้ฟังที่ดี": "A", | |
| "ฉันชอบสิ่งที่แน่นอน คาดเดาได้": "B", | |
| "ฉันมั่นใจในตัวเองสูง": "C", | |
| "ฉันชอบเรื่องตลกขบขัน": "D"}, | |
| 5: {"ฉันชอบการแข่งขัน": "A", | |
| "ฉันไม่ชอบการที่ต้องจริงจังกับชีวิตมากเกินไป": "B", | |
| "ฉันคิดถึงจิตใจผู้อื่นเสมอ": "C", | |
| "ฉันไม่ชอบเสี่ยง ไม่ชอบสิ่งที่ไม่แน่นอน": "D"}, | |
| 6: {"ฉันโน้มน้าวใจคนอื่นเก่ง": "A", | |
| "ฉันเป็นคนอ่อนโยน ขี้เกรงใจ": "B", | |
| "ฉันทำอะไรด้วยความระมัดระวังเสมอ": "C", | |
| "ฉันเป็นคนกล้าหาญและเด็ดเดี่ยว": "D"}, | |
| 7: {"ฉันให้อภัยผู้อื่นเสมอ": "A", | |
| "ฉันเป็นคนมีเหตุผลและเข้มแข็ง": "B", | |
| "ฉันเข้าสังคมเก่ง ชอบติดต่อสื่อสาร": "C", | |
| "ฉันมักตัดสินใจเร็ว กล้าหาญ": "D"}, | |
| 8: {"ฉันปฏิบัติตามกฎระเบียบเสมอ": "A", | |
| "ฉันใจเย็น มีน้ำใจ": "B", | |
| "ฉันเป็นคนมุ่งมั่น ชอบวิเคราะห์": "C", | |
| "ฉันร่าเริง เข้าสังคมเก่ง": "D"}, | |
| 9: {"ฉันชอบที่จะเป็นที่ 1": "A", | |
| "ฉันเป็นคนสุภาพ อัธยาศัยดี": "B", | |
| "ฉันเคารพความคิดเห็นของผู้อื่น": "C", | |
| "ฉันไม่ชอบเข้าสังคม": "D"}, | |
| 10: {"ฉันคิดว่าฉันเป็นคนมีเสน่ห์": "A", | |
| "ฉันวิเคราะห์สถานการณ์ได้ดี": "B", | |
| "ฉันเป็นคนใจร้อน ตัดสินใจเร็ว": "C", | |
| "เวลาจะทำอะไร ฉันจะคิดก่อนว่ามันจะกระทบผู้อื่นหรือไม่": "D"}, | |
| } | |
| # ===================== | |
| # คำอธิบาย DISC | |
| # ===================== | |
| disc_descriptions = { | |
| "D": "กล้าตัดสินใจ มุ่งมั่น ชอบการแข่งขัน และเป็นผู้นำ", | |
| "I": "เข้าสังคมเก่ง ร่าเริง ชอบสื่อสาร โน้มน้าวผู้อื่นได้ดี", | |
| "S": "ใจเย็น อดทน สุภาพ มีน้ำใจ ชอบช่วยเหลือผู้อื่น", | |
| "C": "รอบคอบ มีเหตุผล ชอบความถูกต้อง และทำงานเป็นระบบ", | |
| } | |
| # ===================== | |
| # ฟังก์ชันตรวจสอบปุ่ม Submit | |
| # ===================== | |
| def check_all_selected(affiliation, age_group, *args): | |
| try: | |
| q1 = args[0] | |
| q2 = args[1] | |
| except Exception: | |
| q1 = q2 = None | |
| if affiliation and age_group and q1 and q2: | |
| return gr.update(interactive=True) | |
| else: | |
| return gr.update(interactive=False) | |
| # ===================== | |
| # ฟังก์ชันแสดงผล + บันทึก Excel ใน test/ | |
| # ===================== | |
| def disc_test(affiliation, age_group, *args): | |
| answers_letters = [] | |
| for idx, text in enumerate(args): | |
| key = idx + 1 | |
| ans = questions_text[key].get(text, None) | |
| if ans is None: | |
| ans = 'A' | |
| answers_letters.append(ans) | |
| predictor = DISCPredictor(mapping) | |
| main_trait, percentages = predictor.predict_percentage(answers_letters) | |
| # บันทึกไฟล์ Excel ลง test/ | |
| folder_path = "https://huggingface.co/spaces/hongkokokok/save_data" | |
| os.makedirs(folder_path, exist_ok=True) | |
| file_path = os.path.join(folder_path, "disc_results.xlsx") | |
| ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
| row = { | |
| "timestamp": ts, | |
| "affiliation": affiliation, | |
| "age_group": age_group, | |
| "Main Trait": main_trait, | |
| "D %": percentages.get("D", 0), | |
| "I %": percentages.get("I", 0), | |
| "S %": percentages.get("S", 0), | |
| "C %": percentages.get("C", 0), | |
| } | |
| for i, a in enumerate(answers_letters, start=1): | |
| row[f"Q{i}"] = a | |
| try: | |
| if os.path.exists(file_path): | |
| existing = pd.read_excel(file_path) | |
| df = pd.concat([existing, pd.DataFrame([row])], ignore_index=True) | |
| else: | |
| df = pd.DataFrame([row]) | |
| df.to_excel(file_path, index=False) | |
| saved_msg = f"บันทึกผลเรียบร้อย: {file_path}" | |
| except Exception as e: | |
| saved_msg = f"ไม่สามารถบันทึกไฟล์ Excel ได้: {e}" | |
| # สร้างผลลัพธ์ HTML | |
| result_text = f""" | |
| <h2 style="text-align:center; font-size:2rem; font-weight:700; color:#ff5722; margin-bottom:10px;"> | |
| 🧠 ผลลัพธ์ DISC ของคุณ: <span style="color:#4caf50">{main_trait}</span> | |
| </h2> | |
| <div style="text-align:center; margin-bottom:12px; font-size:0.95rem;"> | |
| <b>สังกัด:</b> {affiliation} | <b>ช่วงอายุ:</b> {age_group} <br> | |
| <small>บันทึกเมื่อ: {ts}</small> | |
| </div> | |
| """ | |
| sorted_traits = sorted(percentages.items(), key=lambda x: x[1], reverse=True) | |
| bg_color_map = {"D": "#ffebee", "I": "#fffde7", "S": "#e8f5e9", "C": "#e3f2fd"} | |
| text_color_map = {"D": "red", "I": "#fbc02d", "S": "green", "C": "blue"} | |
| icon_map = {"D":"🏆", "I":"🎉", "S":"🤝", "C":"📊"} | |
| result_text += "<div style='display:flex; flex-direction:column; gap:12px;'>" | |
| for t, pct in sorted_traits: | |
| desc = disc_descriptions.get(t,"") | |
| result_text += f""" | |
| <div style="background-color:{bg_color_map.get(t,'#f5f5f5')}; | |
| padding:12px; border-radius:12px; box-shadow:1px 1px 6px rgba(0,0,0,0.06); | |
| font-size:1.05rem;"> | |
| <b style="font-size:1.15rem; color:{text_color_map.get(t,'black')};"> | |
| {icon_map.get(t,'')} {t}: {pct}% | |
| </b><br> | |
| <span>{desc}</span> | |
| </div> | |
| """ | |
| result_text += "</div>" | |
| result_text += f"<div style='margin-top:12px; font-size:0.95rem; color:#555;'>{saved_msg}</div>" | |
| return result_text | |
| # ===================== | |
| # Gradio UI | |
| # ===================== | |
| with gr.Blocks() as demo: | |
| gr.HTML(""" | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Kanit:wght@400;600&display=swap'); | |
| @import url('https://fonts.googleapis.com/css2?family=Baloo+2:wght@700&display=swap'); | |
| body { font-family: 'Kanit', sans-serif; } | |
| .big-radio .wrap label { font-size: 1.02rem !important; padding: 10px 14px !important; margin:6px 0 !important; display:block; border:1px solid #ddd; border-radius:10px; transition:0.18s; } | |
| .big-radio .wrap input[type=radio] { transform: scale(1.15); margin-right:8px; } | |
| .radio-question { margin-bottom:18px; padding:14px; border:2px solid #eee; border-radius:12px; background-color:white; box-shadow:0 2px 6px rgba(0,0,0,0.04); transition:0.2s; } | |
| </style> | |
| """) | |
| gr.HTML("<h1 style='text-align:center; font-family:Baloo 2; background:linear-gradient(to right, #ff7e5f, #feb47b); -webkit-background-clip:text; -webkit-text-fill-color:transparent;'>แบบทดสอบ DISC</h1>") | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| affiliation = gr.Dropdown( | |
| choices=["งานการพยาบาลผู้ป่วยกุมารเวชศาสตร์","งานการพยาบาลผู้ป่วยทั่วไป จิตเวชและจักษุ โสต ศอ นาสิก","อื่นๆ"], | |
| label="สังกัด (โปรดเลือก)", | |
| ) | |
| with gr.Column(scale=1): | |
| age_group = gr.Radio( | |
| choices=["น้อยกว่า 20 ปี","21-28 ปี","29-36 ปี","37–44 ปี","45–52 ปี","53-60 ปี"], | |
| label="ช่วงอายุ (โปรดเลือก)", | |
| ) | |
| inputs = [] | |
| for i in range(1, 11): | |
| with gr.Group(elem_classes=["radio-question"]): | |
| gr.HTML(f'<div class="question-number">ข้อ {i}</div>') | |
| radio = gr.Radio( | |
| list(questions_text[i].keys()), | |
| label="", | |
| elem_classes=["big-radio"] | |
| ) | |
| inputs.append(radio) | |
| output = gr.HTML(label="ผลลัพธ์ DISC") | |
| btn = gr.Button("Submit", elem_classes=["submit-btn"], interactive=False) | |
| btn.click(disc_test, inputs=[affiliation, age_group] + inputs, outputs=[output]) | |
| for r in inputs: | |
| r.change(check_all_selected, inputs=[affiliation, age_group] + inputs, outputs=[btn]) | |
| affiliation.change(check_all_selected, inputs=[affiliation, age_group] + inputs, outputs=[btn]) | |
| age_group.change(check_all_selected, inputs=[affiliation, age_group] + inputs, outputs=[btn]) | |
| if __name__ == "__main__": | |
| demo.launch() | |