File size: 28,993 Bytes
f25db82
 
 
8da0f88
 
 
 
 
f25db82
6e4b8e7
f25db82
 
36bff74
 
 
f25db82
36bff74
 
f25db82
 
36bff74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f25db82
 
8da0f88
f25db82
36bff74
 
 
 
 
 
 
 
 
 
 
 
 
8da0f88
36bff74
 
 
 
 
 
 
f25db82
 
 
 
 
8da0f88
36bff74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f25db82
 
36bff74
 
 
 
 
 
f25db82
 
36bff74
 
 
 
f25db82
 
 
 
8da0f88
6e4b8e7
 
 
8da0f88
 
 
 
6e4b8e7
 
 
 
 
 
 
 
8da0f88
 
 
 
6e4b8e7
 
8da0f88
 
 
6e4b8e7
 
8da0f88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6e4b8e7
8da0f88
6e4b8e7
 
8da0f88
6e4b8e7
8da0f88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3bc24b3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6e4b8e7
3bc24b3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8da0f88
3bc24b3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8da0f88
3bc24b3
 
 
 
 
 
 
 
 
 
8da0f88
3bc24b3
 
 
 
8da0f88
3bc24b3
f25db82
3bc24b3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6e4b8e7
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
# 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()