# app.py import gradio as gr import pandas as pd import matplotlib.pyplot as plt import json from io import BytesIO import base64 # TCI_TYPES 데이터 TCI_TYPES = { "Type1": { "name": "활동적인 도전자", "description": "활동적인 도전자는 새로운 경험을 추구하며, 도전적인 상황에서 에너지를 얻습니다. 이들은 창의적이고 모험을 즐기지만 때로는 충동적일 수 있습니다.", "career": ["기업가", "마케팅 전문가", "프로젝트 매니저"], "compatibility": { "good": {"Type2": "보수적인 면을 보완해주어 균형을 이룸."}, "bad": {"Type3": "과도한 활동성으로 인해 감정적으로 충돌할 수 있음."} } }, "Type2": { "name": "신중한 분석가", "description": "신중한 분석가는 세부 사항에 주의를 기울이며, 계획적이고 체계적인 접근을 선호합니다. 이들은 신뢰할 수 있고 책임감이 강하지만 때로는 융통성이 부족할 수 있습니다.", "career": ["데이터 분석가", "회계사", "연구원"], "compatibility": { "good": {"Type1": "활동적인 도전자의 창의성을 보완함."}, "bad": {"Type4": "보수적인 성향으로 인해 갈등이 발생할 수 있음."} } }, "Type3": { "name": "감성적인 예술가", "description": "감성적인 예술가는 감정이 풍부하고 창의성이 뛰어나며, 예술적인 표현을 즐깁니다. 이들은 타인의 감정을 잘 이해하지만 감정 기복이 심할 수 있습니다.", "career": ["작가", "음악가", "디자이너"], "compatibility": { "good": {"Type5": "논리적인 사고를 보완해줌."}, "bad": {"Type1": "과도한 활동성으로 인해 감정적으로 충돌할 수 있음."} } }, "Type4": { "name": "실용적인 관리자", "description": "실용적인 관리자는 조직적이고 효율성을 중시하며, 문제 해결에 뛰어납니다. 이들은 책임감이 강하고 신뢰할 수 있지만 때로는 융통성이 부족할 수 있습니다.", "career": ["운영 관리자", "프로덕트 매니저", "인사 관리자"], "compatibility": { "good": {"Type2": "신중한 분석가와의 조화로 효율성 증대."}, "bad": {"Type6": "과도한 실용주의로 인해 인간관계에서 갈등 발생 가능."} } }, "Type5": { "name": "논리적인 사색가", "description": "논리적인 사색가는 깊이 있는 사고와 분석을 즐기며, 문제 해결에 뛰어납니다. 이들은 독립적이고 창의적이지만 때로는 사회적 상호작용을 어려워할 수 있습니다.", "career": ["소프트웨어 엔지니어", "연구 과학자", "전략 컨설턴트"], "compatibility": { "good": {"Type3": "감성적인 예술가와의 조화로 창의성 증대."}, "bad": {"Type4": "실용적인 관리자와의 목표 충돌 가능."} } }, "Type6": { "name": "친화적인 협력자", "description": "친화적인 협력자는 타인과의 조화를 중시하며, 협동과 배려를 통해 관계를 유지합니다. 이들은 팀워크에 강하지만 때로는 자기주장을 부족하게 할 수 있습니다.", "career": ["인사 담당자", "사회 복지사", "고객 서비스 전문가"], "compatibility": { "good": {"Type7": "개방적인 유형과의 협력을 통해 다양한 시각 수용."}, "bad": {"Type4": "과도한 실용주의로 인해 감정적인 측면에서 갈등 발생 가능."} } }, "Type7": { "name": "개방적인 혁신가", "description": "개방적인 혁신가는 새로운 아이디어와 변화를 추구하며, 유연한 사고를 지니고 있습니다. 이들은 창의적이고 적응력이 뛰어나지만 때로는 집중력이 부족할 수 있습니다.", "career": ["스타트업 창업자", "디지털 마케터", "혁신 컨설턴트"], "compatibility": { "good": {"Type6": "친화적인 협력자와의 조화를 통해 팀워크 강화."}, "bad": {"Type2": "신중한 분석가와의 계획 차이로 인해 갈등 발생 가능."} } } } # 질문 리스트 (TCI-RS) QUESTIONS = [ "1. 쉬운 일보다는 도전적인 일이 더 좋다.", "2. 나를 좋지 않게 대했던 사람들과도 친하게 지낸다.", "3. 음악을 듣거나 산책을 할 때 그 속에 빠져들어 나 자신을 잊는다.", "4. 종종 실제보다 일이 더 어렵거나 위험할 것이라고 예상한다.", "5. 내가 할 수 있는 한 더 잘 하고 싶기 때문에, 열심을 다해 나 자신을 몰아붙인다.", "6. 주머니가 바닥날 때까지 돈을 쓰거나, 신용카드를 너무 많이 사용해서 빚을 지는 때가 있다.", "7. 내가 가지고 있는 많은 습관들은 내가 가치 있는 목표를 달성하는 데 방해가 된다.", "8. 문제가 생기면 혼자서 해결하려 한다.", "9. 미래에 어떤 일이 잘못될까봐 자주 걱정한다.", "10. 별로 힘들이지 않고 하루 종일 활동할 수 있다.", "11. 어려움에 처했거나 뭔가를 필요로 하는 사람이 있을 때 도움을 준다.", "12. 처음 만난 사람과도 편안하게 대화하고 내 의견을 이야기할 수 있다.", "13. 새로운 일에 마음이 쉽게 동해서 당장 시도해 보고 싶어진다.", "14. 다른 아무 것도 눈에 들어오지 않을 정도로 어떤 일에 몰입하여, 때로 딴 세계에 있는 듯 보일 때가 있다.", "15. 종종 나 자신에게 불만을 느낀다.", "16. 내 생각을 받아들이지 않는 사람들을 참아내기가 힘들다.", "17. 낯선 사람을 만날 때, 매우 수줍어하며 위축된다.", "18. 나의 야망과 열심 때문에 성공하는 때가 종종 있다.", "19. 나에게 잘못을 저지른 사람들을 쉽게 용서하는 편이다.", "20. 다른 사람들과 거리를 두고 떨어져 지내는 것이 좋다.", "21. 어떤 일을 할 때 과거에 어떻게 했는지 생각하지 않고 현재의 느낌에 따라 행동한다." ] # 선택지 OPTIONS = ["매우 그렇다", "그렇다", "보통이다", "아니다", "매우 아니다"] # 질문과 TCI 유형 매핑 (실제 TCI 이론에 따라 조정 필요) QUESTION_TYPE_MAPPING = { 0: "Type1", 1: "Type6", 2: "Type3", 3: "Type2", 4: "Type1", 5: "Type4", 6: "Type2", 7: "Type5", 8: "Type2", 9: "Type1", 10: "Type6", 11: "Type7", 12: "Type7", 13: "Type3", 14: "Type3", 15: "Type2", 16: "Type5", 17: "Type1", 18: "Type1", 19: "Type6", 20: "Type7" } def calculate_tci(responses): scores = {type_name: 0 for type_name in TCI_TYPES.keys()} for idx, response in enumerate(responses): type_key = QUESTION_TYPE_MAPPING.get(idx) if type_key: # 선택지 인덱스에 따라 점수 부여 (매우 아니다=1, 매우 그렇다=5) score = response + 1 scores[type_key] += score # 퍼센트 계산 total = sum(scores.values()) if total == 0: percentages = {k: 0 for k in scores.keys()} else: percentages = {k: (v / total * 100) for k, v in scores.items()} # 최고 점수 유형 선택 selected_type = max(percentages, key=percentages.get) return selected_type, percentages[selected_type] def generate_report(tci_data): 개인정보 = tci_data.get("개인정보", {}) 프로파일 = tci_data.get("TCI_RS_프로파일", {}) 하위척도 = tci_data.get("TCI_RS_하위척도", {}) # 개인 정보 섹션 personal_info = f""" ## 개인 정보 - **이름**: {개인정보.get('이름', 'N/A')} - **개인 고유번호**: {개인정보.get('개인고유번호', 'N/A')} - **성별**: {개인정보.get('성별', 'N/A')} - **연령**: {개인정보.get('연령', 'N/A')} - **소속기관 1**: {개인정보.get('소속기관1', 'N/A')} - **소속기관 2**: {개인정보.get('소속기관2', 'N/A')} - **규준집단**: {개인정보.get('규준집단', 'N/A')} - **무응답수**: {개인정보.get('무응답수', 'N/A')} """ # 기질 섹션 temperament = "## 기질\n" for key, value in 프로파일.get("기질", {}).items(): temperament += f"- **{key}**: 원점수={value.get('원점수', 'N/A')}, T점수={value.get('T점수', 'N/A')}, 백분위={value.get('백분위', 'N/A')}\n" # 성격 섹션 personality = "## 성격\n" for key, value in 프로파일.get("성격", {}).items(): personality += f"- **{key}**: 원점수={value.get('원점수', 'N/A')}, T점수={value.get('T점수', 'N/A')}, 백분위={value.get('백분위', 'N/A')}\n" # 하위척도 섹션 subscales = "## TCI-RS 하위척도\n" for scale, subscale_data in 하위척도.items(): subscales += f"### {scale}\n" df = pd.DataFrame.from_dict(subscale_data, orient='index') df = df.reset_index().rename(columns={"index": "하위척도", "원점수": "원점수", "규준집단_M": "규준집단 M", "SD": "표준편차", "특성": "특성"}) subscales += df.to_markdown(index=False) + "\n\n" # 전체 보고서 report = personal_info + "\n" + temperament + "\n" + personality + "\n" + subscales return report def plot_scores(tci_data): scales = [] scores = [] for scale, data in tci_data.get("TCI_RS_프로파일", {}).get("기질", {}).items(): scales.append(scale) scores.append(data.get("백분위", 0)) for scale, data in tci_data.get("TCI_RS_프로파일", {}).get("성격", {}).items(): scales.append(scale) scores.append(data.get("백분위", 0)) plt.figure(figsize=(12, 6)) bars = plt.bar(scales, scores, color='skyblue') plt.xlabel('척도') plt.ylabel('백분위 점수') plt.title('TCI-RS 척도 백분위 점수') plt.ylim(0, 100) plt.axhline(y=30, color='red', linestyle='--', label='30 이하: 낮음') plt.axhline(y=70, color='green', linestyle='--', label='70 이상: 높음') plt.legend() plt.xticks(rotation=45) # 각 막대 위에 점수 표시 for bar in bars: yval = bar.get_height() plt.text(bar.get_x() + bar.get_width()/2.0, yval + 1, f'{yval}', ha='center', va='bottom') plt.tight_layout() # 그래프를 이미지로 저장하지 않고 BytesIO를 사용하여 메모리에 저장 buf = BytesIO() plt.savefig(buf, format='png') buf.seek(0) plt.close() # Gradio에서 직접 이미지를 사용할 수 있도록 base64로 인코딩 image_base64 = base64.b64encode(buf.read()).decode() image_data = f"data:image/png;base64,{image_base64}" return image_data def display_profile(tci_data_json): try: tci_data = json.loads(tci_data_json) except json.JSONDecodeError: return "유효한 JSON 형식이 아닙니다. 다시 시도해주세요.", None report = generate_report(tci_data) image = plot_scores(tci_data) return report, image def main(): # 예시 JSON 데이터 정의 example_data = { "개인정보": { "이름": "홍길동", "개인고유번호": 123, "성별": "남자", "연령": "만 20 세", "소속기관1": "마음사랑", "소속기관2": "", "규준집단": "일반성인", "무응답수": 0 }, "TCI_RS_프로파일": { "기질": { "자극추구(NS)": {"원점수": 34, "T점수": 57, "백분위": 74}, "위험회피(HA)": {"원점수": 40, "T점수": 55, "백분위": 66}, "사회적 민감성(RD)": {"원점수": 41, "T점수": 48, "백분위": 39}, "인내력(PS)": {"원점수": 32, "T점수": 38, "백분위": 11} }, "성격": { "자율성(SD)": {"원점수": 47, "T점수": 49, "백분위": 44}, "연대감(CO)": {"원점수": 43, "T점수": 35, "백분위": 5}, "자기초월(ST)": {"원점수": 27, "T점수": 51, "백분위": 56}, "자율성+연대감(SC)": {"원점수": 90, "T점수": 41, "백분위": 18} } }, "TCI_RS_하위척도": { "자극추구(NS)": { "NS1": {"원점수": 9, "규준집단_M": 9.5, "SD": 3.2, "특성": "관습적안정성 탐색적흥분"}, "NS2": {"원점수": 7, "규준집단_M": 7.0, "SD": 3.3, "특성": "심사숙고 충동성"}, "NS3": {"원점수": 9, "규준집단_M": 6.0, "SD": 3.2, "특성": "절제 무절제"}, "NS4": {"원점수": 9, "규준집단_M": 5.2, "SD": 3.2, "특성": "질서정연 자유분방"} }, "위험회피(HA)": { "HA1": {"원점수": 8, "규준집단_M": 7.8, "SD": 4.1, "특성": "낙천성 예기불안"}, "HA2": {"원점수": 9, "규준집단_M": 9.9, "SD": 3.0, "특성": "(낮은)불확실성에대한두려움 (높은)불확실성에대한두려움"}, "HA3": {"원점수": 12, "규준집단_M": 8.7, "SD": 3.6, "특성": "(낮은)낯선사람에대한수줍음 (높은)낯선사람에대한수줍음"}, "HA4": {"원점수": 11, "규준집단_M": 8.7, "SD": 3.4, "특성": "활기넘침 쉽게지침"} }, "사회적 민감성(RD)": { "RD1": {"원점수": 8, "규준집단_M": 11.1, "SD": 2.9, "특성": "(낮은)정서적감수성 (높은)정서적감수성"}, "RD2": {"원점수": 10, "규준집단_M": 10.5, "SD": 3.0, "특성": "(낮은)정서적개방성 (높은)정서적개방성"}, "RD3": {"원점수": 13, "규준집단_M": 11.6, "SD": 3.3, "특성": "거리두기 친밀감"}, "RD4": {"원점수": 10, "규준집단_M": 9.4, "SD": 2.6, "특성": "독립 의존"} }, "인내력(PS)": { "PS1": {"원점수": 12, "규준집단_M": 12.7, "SD": 3.0, "특성": "(낮은)근면 (높은)근면"}, "PS2": {"원점수": 6, "규준집단_M": 10.7, "SD": 3.0, "특성": "(낮은)끈기 (높은)끈기"}, "PS3": {"원점수": 7, "규준집단_M": 10.4, "SD": 3.7, "특성": "(낮은)성취에대한야망 (높은)성취에대한야망"}, "PS4": {"원점수": 7, "규준집단_M": 9.9, "SD": 3.5, "특성": "(낮은)완벽주의 (높은)완벽주의"} }, "자율성(SD)": { "SD1": {"원점수": 12, "규준집단_M": 12.7, "SD": 2.9, "특성": "(낮은)책임감 (높은)책임감"}, "SD2": {"원점수": 12, "규준집단_M": 11.6, "SD": 3.1, "특성": "(낮은)목적의식 (높은)목적의식"}, "SD3": {"원점수": 6, "규준집단_M": 6.8, "SD": 1.9, "특성": "(낮은)유능감 (높은)유능감"}, "SD4": {"원점수": 5, "규준집단_M": 4.2, "SD": 1.7, "특성": "(낮은)자기수용 (높은)자기수용"}, "SD5": {"원점수": 12, "규준집단_M": 12.5, "SD": 3.2, "특성": "(낮은)자기일치 (높은)자기일치"} }, "연대감(CO)": { "CO1": {"원점수": 11, "규준집단_M": 12.6, "SD": 2.8, "특성": "(낮은)타인수용 (높은)타인수용"}, "CO2": {"원점수": 6, "규준집단_M": 9.6, "SD": 2.5, "특성": "(낮은)공감 (높은)공감"}, "CO3": {"원점수": 7, "규준집단_M": 9.9, "SD": 2.5, "특성": "(낮은)이타성 (높은)이타성"}, "CO4": {"원점수": 9, "규준집단_M": 8.9, "SD": 2.6, "특성": "(낮은)관대함 (높은)관대함"}, "CO5": {"원점수": 10, "규준집단_M": 15.1, "SD": 2.5, "특성": "(낮은)공평 (높은)공평"} }, "자기초월(ST)": { "ST1": {"원점수": 10, "규준집단_M": 9.1, "SD": 4.0, "특성": "자의식 창조적자기망각"}, "ST2": {"원점수": 8, "규준집단_M": 7.4, "SD": 4.1, "특성": "(낮은)우주만물과의일체감 (높은)우주만물과의일체감"}, "ST3": {"원점수": 9, "규준집단_M": 9.2, "SD": 5.4, "특성": "합리적유물론 영성수용"} } } } def plot_scores(tci_data): scales = [] scores = [] for scale, data in tci_data.get("TCI_RS_프로파일", {}).get("기질", {}).items(): scales.append(scale) scores.append(data.get("백분위", 0)) for scale, data in tci_data.get("TCI_RS_프로파일", {}).get("성격", {}).items(): scales.append(scale) scores.append(data.get("백분위", 0)) plt.figure(figsize=(12, 6)) bars = plt.bar(scales, scores, color='skyblue') plt.xlabel('척도') plt.ylabel('백분위 점수') plt.title('TCI-RS 척도 백분위 점수') plt.ylim(0, 100) plt.axhline(y=30, color='red', linestyle='--', label='30 이하: 낮음') plt.axhline(y=70, color='green', linestyle='--', label='70 이상: 높음') plt.legend() plt.xticks(rotation=45) # 각 막대 위에 점수 표시 for bar in bars: yval = bar.get_height() plt.text(bar.get_x() + bar.get_width()/2.0, yval + 1, f'{yval}', ha='center', va='bottom') plt.tight_layout() # 그래프를 이미지로 저장하지 않고 BytesIO를 사용하여 메모리에 저장 buf = BytesIO() plt.savefig(buf, format='png') buf.seek(0) plt.close() # Gradio에서 직접 이미지를 사용할 수 있도록 base64로 인코딩 image_base64 = base64.b64encode(buf.read()).decode() image_data = f"data:image/png;base64,{image_base64}" return image_data def display_profile(tci_data_json): try: tci_data = json.loads(tci_data_json) except json.JSONDecodeError: return "유효한 JSON 형식이 아닙니다. 다시 시도해주세요.", None report = generate_report(tci_data) image = plot_scores(tci_data) return report, image def main(): # 예시 JSON 데이터 정의 example_data = { "개인정보": { "이름": "홍길동", "개인고유번호": 123, "성별": "남자", "연령": "만 20 세", "소속기관1": "마음사랑", "소속기관2": "", "규준집단": "일반성인", "무응답수": 0 }, "TCI_RS_프로파일": { "기질": { "자극추구(NS)": {"원점수": 34, "T점수": 57, "백분위": 74}, "위험회피(HA)": {"원점수": 40, "T점수": 55, "백분위": 66}, "사회적 민감성(RD)": {"원점수": 41, "T점수": 48, "백분위": 39}, "인내력(PS)": {"원점수": 32, "T점수": 38, "백분위": 11} }, "성격": { "자율성(SD)": {"원점수": 47, "T점수": 49, "백분위": 44}, "연대감(CO)": {"원점수": 43, "T점수": 35, "백분위": 5}, "자기초월(ST)": {"원점수": 27, "T점수": 51, "백분위": 56}, "자율성+연대감(SC)": {"원점수": 90, "T점수": 41, "백분위": 18} } }, "TCI_RS_하위척도": { "자극추구(NS)": { "NS1": {"원점수": 9, "규준집단_M": 9.5, "SD": 3.2, "특성": "관습적안정성 탐색적흥분"}, "NS2": {"원점수": 7, "규준집단_M": 7.0, "SD": 3.3, "특성": "심사숙고 충동성"}, "NS3": {"원점수": 9, "규준집단_M": 6.0, "SD": 3.2, "특성": "절제 무절제"}, "NS4": {"원점수": 9, "규준집단_M": 5.2, "SD": 3.2, "특성": "질서정연 자유분방"} }, "위험회피(HA)": { "HA1": {"원점수": 8, "규준집단_M": 7.8, "SD": 4.1, "특성": "낙천성 예기불안"}, "HA2": {"원점수": 9, "규준집단_M": 9.9, "SD": 3.0, "특성": "(낮은)불확실성에대한두려움 (높은)불확실성에대한두려움"}, "HA3": {"원점수": 12, "규준집단_M": 8.7, "SD": 3.6, "특성": "(낮은)낯선사람에대한수줍음 (높은)낯선사람에대한수줍음"}, "HA4": {"원점수": 11, "규준집단_M": 8.7, "SD": 3.4, "특성": "활기넘침 쉽게지침"} }, "사회적 민감성(RD)": { "RD1": {"원점수": 8, "규준집단_M": 11.1, "SD": 2.9, "특성": "(낮은)정서적감수성 (높은)정서적감수성"}, "RD2": {"원점수": 10, "규준집단_M": 10.5, "SD": 3.0, "특성": "(낮은)정서적개방성 (높은)정서적개방성"}, "RD3": {"원점수": 13, "규준집단_M": 11.6, "SD": 3.3, "특성": "거리두기 친밀감"}, "RD4": {"원점수": 10, "규준집단_M": 9.4, "SD": 2.6, "특성": "독립 의존"} }, "인내력(PS)": { "PS1": {"원점수": 12, "규준집단_M": 12.7, "SD": 3.0, "특성": "(낮은)근면 (높은)근면"}, "PS2": {"원점수": 6, "규준집단_M": 10.7, "SD": 3.0, "특성": "(낮은)끈기 (높은)끈기"}, "PS3": {"원점수": 7, "규준집단_M": 10.4, "SD": 3.7, "특성": "(낮은)성취에대한야망 (높은)성취에대한야망"}, "PS4": {"원점수": 7, "규준집단_M": 9.9, "SD": 3.5, "특성": "(낮은)완벽주의 (높은)완벽주의"} }, "자율성(SD)": { "SD1": {"원점수": 12, "규준집단_M": 12.7, "SD": 2.9, "특성": "(낮은)책임감 (높은)책임감"}, "SD2": {"원점수": 12, "규준집단_M": 11.6, "SD": 3.1, "특성": "(낮은)목적의식 (높은)목적의식"}, "SD3": {"원점수": 6, "규준집단_M": 6.8, "SD": 1.9, "특성": "(낮은)유능감 (높은)유능감"}, "SD4": {"원점수": 5, "규준집단_M": 4.2, "SD": 1.7, "특성": "(낮은)자기수용 (높은)자기수용"}, "SD5": {"원점수": 12, "규준집단_M": 12.5, "SD": 3.2, "특성": "(낮은)자기일치 (높은)자기일치"} }, "연대감(CO)": { "CO1": {"원점수": 11, "규준집단_M": 12.6, "SD": 2.8, "특성": "(낮은)타인수용 (높은)타인수용"}, "CO2": {"원점수": 6, "규준집단_M": 9.6, "SD": 2.5, "특성": "(낮은)공감 (높은)공감"}, "CO3": {"원점수": 7, "규준집단_M": 9.9, "SD": 2.5, "특성": "(낮은)이타성 (높은)이타성"}, "CO4": {"원점수": 9, "규준집단_M": 8.9, "SD": 2.6, "특성": "(낮은)관대함 (높은)관대함"}, "CO5": {"원점수": 10, "규준집단_M": 15.1, "SD": 2.5, "특성": "(낮은)공평 (높은)공평"} }, "자기초월(ST)": { "ST1": {"원점수": 10, "규준집단_M": 9.1, "SD": 4.0, "특성": "자의식 창조적자기망각"}, "ST2": {"원점수": 8, "규준집단_M": 7.4, "SD": 4.1, "특성": "(낮은)우주만물과의일체감 (높은)우주만물과의일체감"}, "ST3": {"원점수": 9, "규준집단_M": 9.2, "SD": 5.4, "특성": "합리적유물론 영성수용"} } } } def generate_report(tci_data): 개인정보 = tci_data.get("개인정보", {}) 프로파일 = tci_data.get("TCI_RS_프로파일", {}) 하위척도 = tci_data.get("TCI_RS_하위척도", {}) # 개인 정보 섹션 personal_info = f""" ## 개인 정보 - **이름**: {개인정보.get('이름', 'N/A')} - **개인 고유번호**: {개인정보.get('개인고유번호', 'N/A')} - **성별**: {개인정보.get('성별', 'N/A')} - **연령**: {개인정보.get('연령', 'N/A')} - **소속기관 1**: {개인정보.get('소속기관1', 'N/A')} - **소속기관 2**: {개인정보.get('소속기관2', 'N/A')} - **규준집단**: {개인정보.get('규준집단', 'N/A')} - **무응답수**: {개인정보.get('무응답수', 'N/A')} """ # 기질 섹션 temperament = "## 기질\n" for key, value in 프로파일.get("기질", {}).items(): temperament += f"- **{key}**: 원점수={value.get('원점수', 'N/A')}, T점수={value.get('T점수', 'N/A')}, 백분위={value.get('백분위', 'N/A')}\n" # 성격 섹션 personality = "## 성격\n" for key, value in 프로파일.get("성격", {}).items(): personality += f"- **{key}**: 원점수={value.get('원점수', 'N/A')}, T점수={value.get('T점수', 'N/A')}, 백분위={value.get('백분위', 'N/A')}\n" # 하위척도 섹션 subscales = "## TCI-RS 하위척도\n" for scale, subscale_data in 하위척도.items(): subscales += f"### {scale}\n" df = pd.DataFrame.from_dict(subscale_data, orient='index') df = df.reset_index().rename(columns={"index": "하위척도", "원점수": "원점수", "규준집단_M": "규준집단 M", "SD": "표준편차", "특성": "특성"}) subscales += df.to_markdown(index=False) + "\n\n" # 전체 보고서 report = personal_info + "\n" + temperament + "\n" + personality + "\n" + subscales return report def plot_scores(tci_data): scales = [] scores = [] for scale, data in tci_data.get("TCI_RS_프로파일", {}).get("기질", {}).items(): scales.append(scale) scores.append(data.get("백분위", 0)) for scale, data in tci_data.get("TCI_RS_프로파일", {}).get("성격", {}).items(): scales.append(scale) scores.append(data.get("백분위", 0)) plt.figure(figsize=(12, 6)) bars = plt.bar(scales, scores, color='skyblue') plt.xlabel('척도') plt.ylabel('백분위 점수') plt.title('TCI-RS 척도 백분위 점수') plt.ylim(0, 100) plt.axhline(y=30, color='red', linestyle='--', label='30 이하: 낮음') plt.axhline(y=70, color='green', linestyle='--', label='70 이상: 높음') plt.legend() plt.xticks(rotation=45) # 각 막대 위에 점수 표시 for bar in bars: yval = bar.get_height() plt.text(bar.get_x() + bar.get_width()/2.0, yval + 1, f'{yval}', ha='center', va='bottom') plt.tight_layout() # 그래프를 이미지로 저장하지 않고 BytesIO를 사용하여 메모리에 저장 buf = BytesIO() plt.savefig(buf, format='png') buf.seek(0) plt.close() # Gradio에서 직접 이미지를 사용할 수 있도록 base64로 인코딩 image_base64 = base64.b64encode(buf.read()).decode() image_data = f"data:image/png;base64,{image_base64}" return image_data def display_profile(tci_data_json): try: tci_data = json.loads(tci_data_json) except json.JSONDecodeError: return "유효한 JSON 형식이 아닙니다. 다시 시도해주세요.", None report = generate_report(tci_data) image = plot_scores(tci_data) return report, image def main(): with gr.Blocks() as demo: gr.Markdown("# TCI-RS 프로파일 보고서") gr.Markdown( f""" 아래에 TCI-RS 프로파일 데이터를 JSON 형식으로 입력해주세요. **예시 JSON 데이터** ```json {json.dumps(example_data, ensure_ascii=False, indent=4)} ``` """ ) input_json = gr.Textbox( label="TCI-RS 데이터 (JSON)", lines=15, placeholder="TCI-RS 데이터를 JSON 형식으로 입력해주세요.", value=json.dumps(example_data, ensure_ascii=False, indent=4) ) submit = gr.Button("보고서 생성") report_output = gr.Markdown() image_output = gr.Image() submit.click(fn=display_profile, inputs=input_json, outputs=[report_output, image_output]) demo.launch() if __name__ == "__main__": main()