Update app.py
Browse files
app.py
CHANGED
@@ -11,51 +11,50 @@ from zoneinfo import ZoneInfo
|
|
11 |
# OpenAI API 키를 환경변수에서 가져오기
|
12 |
API_KEY = os.getenv("API_KEY")
|
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 |
-
def
|
40 |
-
print(f"{exception_type.__name__}: {exception}")
|
41 |
-
|
42 |
-
sys.excepthook = exception_handler
|
43 |
-
sys.tracebacklimit = 0
|
44 |
-
|
45 |
-
def convert_to_seoul_time(year, month, day, hour, minute, selected_timezone):
|
46 |
"""
|
47 |
선택된 시간대의 시간을 서울 시간으로 변환
|
48 |
"""
|
49 |
-
|
50 |
-
local_tz = pytz.timezone(TIMEZONE_CITIES[selected_timezone])
|
51 |
-
seoul_tz = pytz.timezone('Asia/Seoul')
|
52 |
|
53 |
-
#
|
54 |
-
|
55 |
-
int(hour), int(minute)))
|
56 |
|
57 |
-
#
|
58 |
-
|
|
|
|
|
|
|
59 |
|
60 |
return {
|
61 |
'year': str(seoul_time.year),
|
@@ -64,6 +63,13 @@ def convert_to_seoul_time(year, month, day, hour, minute, selected_timezone):
|
|
64 |
'hour': f"{seoul_time.hour:02d}{seoul_time.minute:02d}"
|
65 |
}
|
66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
def get_chinese_hour(birth_hour):
|
68 |
hour = int(birth_hour[:2])
|
69 |
chinese_hours = ['자', '축', '인', '묘', '진', '사', '오', '미', '신', '유', '술', '해']
|
@@ -151,14 +157,16 @@ def calculate_siju(birth_hour, lunIljin):
|
|
151 |
|
152 |
return siju
|
153 |
|
154 |
-
|
|
|
155 |
try:
|
156 |
# 시간 파싱
|
157 |
hour = int(solHour[:2])
|
158 |
minute = int(solHour[2:]) if len(solHour) > 2 else 0
|
159 |
|
160 |
# 선택된 시간대에서 서울 시간으로 변환
|
161 |
-
seoul_time = convert_to_seoul_time(solYear, solMonth, solDay, hour, minute,
|
|
|
162 |
|
163 |
base_url = "http://apis.data.go.kr/B090041/openapi/service/LrsrCldInfoService/getLunCalInfo"
|
164 |
service_key = "EuBjt%2B2t0KzYt5Ri%2Bzs56oymt9kHJ8uPIOV7m6UMKnOV8cpwj02EiRqMEudX%2F%2FVTKdMnwaLH0igRwY57YUplEQ%3D%3D"
|
@@ -350,6 +358,7 @@ button {
|
|
350 |
button:hover {
|
351 |
transform: translateY(-2px);
|
352 |
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
|
|
353 |
}
|
354 |
|
355 |
/* 입력 컨테이너 스타일 */
|
@@ -359,6 +368,12 @@ button:hover {
|
|
359 |
padding: 20px;
|
360 |
margin: 10px 0;
|
361 |
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
|
|
|
|
|
|
|
|
|
|
|
|
362 |
}
|
363 |
|
364 |
/* 출력 컨테이너 스타일 */
|
@@ -370,96 +385,86 @@ button:hover {
|
|
370 |
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
371 |
}
|
372 |
|
373 |
-
/* 시간대
|
374 |
-
.timezone-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
margin: 10px 0;
|
379 |
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
380 |
-
}
|
381 |
-
|
382 |
-
/* 분석 결과 스타일 */
|
383 |
-
.analysis-result {
|
384 |
-
background: white;
|
385 |
border-radius: 10px;
|
386 |
-
padding: 20px;
|
387 |
-
margin: 15px 0;
|
388 |
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
389 |
}
|
390 |
|
|
|
|
|
|
|
|
|
|
|
391 |
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
|
399 |
-
backdrop-filter: blur(4px);
|
400 |
-
border: 1px solid rgba(255, 255, 255, 0.18);
|
401 |
-
padding: 20px;
|
402 |
-
margin: 10px;
|
403 |
}
|
404 |
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
padding: 12px 24px;
|
411 |
-
text-align: center;
|
412 |
-
font-size: 16px;
|
413 |
-
border-radius: 8px;
|
414 |
-
cursor: pointer;
|
415 |
-
transition: all 0.3s ease;
|
416 |
-
font-weight: bold;
|
417 |
-
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
418 |
}
|
419 |
|
420 |
-
|
421 |
-
|
422 |
-
|
|
|
|
|
|
|
423 |
}
|
424 |
|
425 |
-
/*
|
426 |
-
.
|
427 |
-
background:
|
428 |
-
border-radius:
|
429 |
-
padding:
|
430 |
-
margin:
|
431 |
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
|
|
432 |
}
|
433 |
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
border-radius: 10px;
|
438 |
-
padding: 20px;
|
439 |
-
margin: 10px 0;
|
440 |
-
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
441 |
}
|
442 |
|
443 |
/* 큰 출력 박스 스타일 */
|
444 |
.large-output-box {
|
445 |
height: 500px !important;
|
446 |
font-size: 16px !important;
|
447 |
-
padding:
|
448 |
background: linear-gradient(135deg, #f6f9fc, #eef2f7);
|
449 |
border-radius: 15px;
|
450 |
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
451 |
margin: 20px 0;
|
452 |
overflow-y: auto;
|
453 |
-
line-height: 1.
|
454 |
white-space: pre-wrap;
|
|
|
|
|
|
|
|
|
|
|
|
|
455 |
}
|
456 |
|
457 |
/* 탭 내비게이션 스타일 */
|
458 |
.tab-nav {
|
459 |
background: rgba(255, 255, 255, 0.9);
|
460 |
-
border-radius:
|
461 |
-
padding:
|
462 |
-
margin-bottom:
|
|
|
463 |
}
|
464 |
|
465 |
/* 마크다운 텍스트 스타일 */
|
@@ -467,34 +472,59 @@ button:hover {
|
|
467 |
text-align: center;
|
468 |
color: #2d3748;
|
469 |
text-shadow: 1px 1px 2px rgba(0,0,0,0.1);
|
|
|
|
|
470 |
}
|
471 |
|
472 |
/* 신년운세 특별 스타일 */
|
473 |
.newyear-container {
|
474 |
background: linear-gradient(135deg, #ffd1d1, #ffecd1);
|
475 |
-
padding:
|
476 |
border-radius: 20px;
|
477 |
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
|
478 |
-
margin:
|
|
|
|
|
|
|
|
|
|
|
|
|
479 |
}
|
480 |
|
481 |
.newyear-title {
|
482 |
-
font-size:
|
483 |
color: #2d3748;
|
484 |
text-align: center;
|
485 |
-
margin-bottom:
|
486 |
text-shadow: 1px 1px 2px rgba(0,0,0,0.1);
|
|
|
487 |
}
|
488 |
|
489 |
/* 탭 컨텐츠 스타일 */
|
490 |
.tab-content {
|
491 |
min-height: 600px;
|
492 |
-
padding:
|
|
|
|
|
|
|
493 |
}
|
494 |
-
"""
|
495 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
496 |
|
497 |
-
# Gradio
|
498 |
with gr.Blocks(css=css) as demo:
|
499 |
gr.Markdown("# 🔮 AI 사주 운세 분석 시스템 ✨", elem_id="title")
|
500 |
gr.Markdown("### 🌟 양력 생년월일시와 출생 지역을 입력하시면 AI가 당신의 운세를 분석해드립니다! 💫", elem_id="description1")
|
@@ -509,12 +539,18 @@ with gr.Blocks(css=css) as demo:
|
|
509 |
label="⏰ 생시 (예: 1030)",
|
510 |
placeholder="24시간 형식으로 입력 (예: 2230)"
|
511 |
)
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
518 |
)
|
519 |
|
520 |
with gr.Group(elem_classes="output-container"):
|
@@ -523,6 +559,10 @@ with gr.Blocks(css=css) as demo:
|
|
523 |
lunIljin_state = gr.State()
|
524 |
|
525 |
get_lunar_info_btn = gr.Button("✨ 운세 분석하기")
|
|
|
|
|
|
|
|
|
526 |
|
527 |
with gr.Tab("🧐 성격과 특징 분석"):
|
528 |
with gr.Group(elem_classes="output-container"):
|
@@ -547,13 +587,17 @@ with gr.Blocks(css=css) as demo:
|
|
547 |
)
|
548 |
analyze_button2 = gr.Button("🎯 2025년 운세 보기", size="large")
|
549 |
|
|
|
|
|
|
|
550 |
# 이벤트 연결
|
551 |
get_lunar_info_btn.click(
|
552 |
fn=get_lunar_info,
|
553 |
-
inputs=[solYear, solMonth, solDay, solHour,
|
554 |
outputs=[output1, lunIljin_state, analysis_result]
|
555 |
)
|
556 |
-
|
|
|
557 |
analyze_button.click(
|
558 |
fn=analyze_character,
|
559 |
inputs=[lunIljin_state],
|
@@ -570,77 +614,3 @@ with gr.Blocks(css=css) as demo:
|
|
570 |
demo.launch()
|
571 |
|
572 |
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
# Gradio 인터페이스
|
584 |
-
with gr.Blocks(css=css) as demo:
|
585 |
-
gr.Markdown("# 🔮 AI 사주 운세 분석 시스템 ✨", elem_id="title")
|
586 |
-
gr.Markdown("### 🌟 양력 생년월일시를 입력하시면 AI가 당신의 운세를 분석해드립니다! 💫", elem_id="description1")
|
587 |
-
gr.Markdown("### 💫 커뮤니티: https://discord.gg/openfreeai 🌈", elem_id="description3")
|
588 |
-
|
589 |
-
with gr.Tab("📝 기본 정보 입력 및 요약"):
|
590 |
-
with gr.Group(elem_classes="input-container"):
|
591 |
-
solYear = gr.Textbox(label="🎂 생년 (예: 1990)")
|
592 |
-
solMonth = gr.Textbox(label="📅 생월 (예: 01)")
|
593 |
-
solDay = gr.Textbox(label="📆 생일 (예: 31)")
|
594 |
-
solHour = gr.Textbox(
|
595 |
-
label="⏰ 생시 (예: 1030)",
|
596 |
-
placeholder="24시간 형식으로 입력 (예: 2230)"
|
597 |
-
)
|
598 |
-
|
599 |
-
with gr.Group(elem_classes="output-container"):
|
600 |
-
output1 = gr.JSON(label="🎯 사주 분석 결과")
|
601 |
-
analysis_result = gr.HTML()
|
602 |
-
lunIljin_state = gr.State()
|
603 |
-
|
604 |
-
get_lunar_info_btn = gr.Button("✨ 운세 분석하기")
|
605 |
-
|
606 |
-
with gr.Tab("🧐 성격과 특징 분석"):
|
607 |
-
with gr.Group(elem_classes="output-container"):
|
608 |
-
character_output = gr.Textbox(
|
609 |
-
label="🎭 당신의 성격과 특징",
|
610 |
-
elem_classes="large-output-box"
|
611 |
-
)
|
612 |
-
analyze_button = gr.Button("🔍 성격 분석하기")
|
613 |
-
|
614 |
-
with gr.Tab("🎊 2025년 운세"):
|
615 |
-
with gr.Group(elem_classes="newyear-container"):
|
616 |
-
gr.Markdown("### 🌟 2025년 을사년 신년운세 🌟", elem_classes="newyear-title")
|
617 |
-
newyear_output = gr.Textbox(
|
618 |
-
label="🎉 상세 운세 분석",
|
619 |
-
elem_classes="large-output-box",
|
620 |
-
lines=20,
|
621 |
-
max_lines=30,
|
622 |
-
show_label=True,
|
623 |
-
placeholder="운세 분석 결과가 여기에 표시됩니다..."
|
624 |
-
)
|
625 |
-
analyze_button2 = gr.Button("🎯 2025년 운세 보기", size="large")
|
626 |
-
|
627 |
-
# 이벤트 연결
|
628 |
-
get_lunar_info_btn.click(
|
629 |
-
fn=get_lunar_info,
|
630 |
-
inputs=[solYear, solMonth, solDay, solHour],
|
631 |
-
outputs=[output1, lunIljin_state, analysis_result]
|
632 |
-
)
|
633 |
-
|
634 |
-
analyze_button.click(
|
635 |
-
fn=analyze_character,
|
636 |
-
inputs=[lunIljin_state],
|
637 |
-
outputs=character_output
|
638 |
-
)
|
639 |
-
|
640 |
-
analyze_button2.click(
|
641 |
-
fn=analyze_2024,
|
642 |
-
inputs=[lunIljin_state],
|
643 |
-
outputs=newyear_output
|
644 |
-
)
|
645 |
-
|
646 |
-
demo.launch()
|
|
|
11 |
# OpenAI API 키를 환경변수에서 가져오기
|
12 |
API_KEY = os.getenv("API_KEY")
|
13 |
|
14 |
+
TIMEZONE_MAPPING = {
|
15 |
+
-12: "태평양/베이커 섬 (UTC-12)",
|
16 |
+
-11: "태평양/미드웨이 (UTC-11)",
|
17 |
+
-10: "태평양/호놀룰루 (UTC-10)",
|
18 |
+
-9: "미국/앵커리지 (UTC-9)",
|
19 |
+
-8: "미국/로스앤젤레스 (UTC-8)",
|
20 |
+
-7: "미국/덴버 (UTC-7)",
|
21 |
+
-6: "미국/시카고 (UTC-6)",
|
22 |
+
-5: "미국/뉴욕 (UTC-5)",
|
23 |
+
-4: "캐나다/핼리팩스 (UTC-4)",
|
24 |
+
-3: "브라질/상파울루 (UTC-3)",
|
25 |
+
-2: "대서양/남조지아 (UTC-2)",
|
26 |
+
-1: "대서양/아조레스 (UTC-1)",
|
27 |
+
0: "영국/런던 (UTC+0)",
|
28 |
+
1: "유럽/파리 (UTC+1)",
|
29 |
+
2: "유럽/아테네 (UTC+2)",
|
30 |
+
3: "유럽/모스크바 (UTC+3)",
|
31 |
+
4: "아시아/두바이 (UTC+4)",
|
32 |
+
5: "아시아/카라치 (UTC+5)",
|
33 |
+
6: "아시아/다카 (UTC+6)",
|
34 |
+
7: "아시아/방콕 (UTC+7)",
|
35 |
+
8: "아시아/싱가포르 (UTC+8)",
|
36 |
+
9: "아시아/서울 (UTC+9)",
|
37 |
+
10: "아시아/시드니 (UTC+10)",
|
38 |
+
11: "태평양/누메아 (UTC+11)",
|
39 |
+
12: "태평양/오클랜드 (UTC+12)",
|
40 |
+
13: "태평양/통가 (UTC+13)",
|
41 |
+
14: "태평양/키리티마티 (UTC+14)"
|
42 |
}
|
43 |
|
44 |
+
def convert_to_seoul_time(year, month, day, hour, minute, timezone_offset):
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
"""
|
46 |
선택된 시간대의 시간을 서울 시간으로 변환
|
47 |
"""
|
48 |
+
from datetime import datetime, timedelta
|
|
|
|
|
49 |
|
50 |
+
# 서울과의 시차 계산 (서울은 UTC+9)
|
51 |
+
time_difference = 9 - int(timezone_offset)
|
|
|
52 |
|
53 |
+
# 입력 시간 생성
|
54 |
+
local_time = datetime(int(year), int(month), int(day), int(hour), int(minute))
|
55 |
+
|
56 |
+
# 시차 적용
|
57 |
+
seoul_time = local_time + timedelta(hours=time_difference)
|
58 |
|
59 |
return {
|
60 |
'year': str(seoul_time.year),
|
|
|
63 |
'hour': f"{seoul_time.hour:02d}{seoul_time.minute:02d}"
|
64 |
}
|
65 |
|
66 |
+
def exception_handler(exception_type, exception, traceback):
|
67 |
+
print(f"{exception_type.__name__}: {exception}")
|
68 |
+
|
69 |
+
sys.excepthook = exception_handler
|
70 |
+
sys.tracebacklimit = 0
|
71 |
+
|
72 |
+
|
73 |
def get_chinese_hour(birth_hour):
|
74 |
hour = int(birth_hour[:2])
|
75 |
chinese_hours = ['자', '축', '인', '묘', '진', '사', '오', '미', '신', '유', '술', '해']
|
|
|
157 |
|
158 |
return siju
|
159 |
|
160 |
+
# get_lunar_info 함수 수정
|
161 |
+
def get_lunar_info(solYear, solMonth, solDay, solHour, timezone_offset):
|
162 |
try:
|
163 |
# 시간 파싱
|
164 |
hour = int(solHour[:2])
|
165 |
minute = int(solHour[2:]) if len(solHour) > 2 else 0
|
166 |
|
167 |
# 선택된 시간대에서 서울 시간으로 변환
|
168 |
+
seoul_time = convert_to_seoul_time(solYear, solMonth, solDay, hour, minute, timezone_offset)
|
169 |
+
|
170 |
|
171 |
base_url = "http://apis.data.go.kr/B090041/openapi/service/LrsrCldInfoService/getLunCalInfo"
|
172 |
service_key = "EuBjt%2B2t0KzYt5Ri%2Bzs56oymt9kHJ8uPIOV7m6UMKnOV8cpwj02EiRqMEudX%2F%2FVTKdMnwaLH0igRwY57YUplEQ%3D%3D"
|
|
|
358 |
button:hover {
|
359 |
transform: translateY(-2px);
|
360 |
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
|
361 |
+
background: linear-gradient(135deg, #FF8E53, #FF6B6B);
|
362 |
}
|
363 |
|
364 |
/* 입력 컨테이너 스타일 */
|
|
|
368 |
padding: 20px;
|
369 |
margin: 10px 0;
|
370 |
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
371 |
+
transition: all 0.3s ease;
|
372 |
+
}
|
373 |
+
|
374 |
+
.input-container:hover {
|
375 |
+
transform: translateY(-2px);
|
376 |
+
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
|
377 |
}
|
378 |
|
379 |
/* 출력 컨테이너 스타일 */
|
|
|
385 |
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
386 |
}
|
387 |
|
388 |
+
/* 시간대 슬라이더 스타일 */
|
389 |
+
.timezone-slider {
|
390 |
+
margin: 20px 0;
|
391 |
+
padding: 15px;
|
392 |
+
background: linear-gradient(135deg, #ffffff, #f8f9fa);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
393 |
border-radius: 10px;
|
|
|
|
|
394 |
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
395 |
}
|
396 |
|
397 |
+
.timezone-slider .slider {
|
398 |
+
height: 10px !important;
|
399 |
+
background: linear-gradient(90deg, #6e8efb, #a777e3) !important;
|
400 |
+
border-radius: 5px !important;
|
401 |
+
}
|
402 |
|
403 |
+
.timezone-slider .thumb {
|
404 |
+
width: 20px !important;
|
405 |
+
height: 20px !important;
|
406 |
+
background: white !important;
|
407 |
+
border: 2px solid #6e8efb !important;
|
408 |
+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2) !important;
|
|
|
|
|
|
|
|
|
|
|
409 |
}
|
410 |
|
411 |
+
.timezone-slider .label {
|
412 |
+
font-size: 14px;
|
413 |
+
font-weight: 500;
|
414 |
+
color: #2d3748;
|
415 |
+
margin-bottom: 10px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
416 |
}
|
417 |
|
418 |
+
#selected-timezone-display {
|
419 |
+
text-align: center;
|
420 |
+
font-size: 14px;
|
421 |
+
color: #4a5568;
|
422 |
+
margin-top: 5px;
|
423 |
+
font-weight: 500;
|
424 |
}
|
425 |
|
426 |
+
/* 분석 결과 스타일 */
|
427 |
+
.analysis-result {
|
428 |
+
background: white;
|
429 |
+
border-radius: 15px;
|
430 |
+
padding: 25px;
|
431 |
+
margin: 20px 0;
|
432 |
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
433 |
+
transition: all 0.3s ease;
|
434 |
}
|
435 |
|
436 |
+
.analysis-result:hover {
|
437 |
+
transform: translateY(-2px);
|
438 |
+
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
|
|
|
|
|
|
|
|
439 |
}
|
440 |
|
441 |
/* 큰 출력 박스 스타일 */
|
442 |
.large-output-box {
|
443 |
height: 500px !important;
|
444 |
font-size: 16px !important;
|
445 |
+
padding: 25px !important;
|
446 |
background: linear-gradient(135deg, #f6f9fc, #eef2f7);
|
447 |
border-radius: 15px;
|
448 |
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
449 |
margin: 20px 0;
|
450 |
overflow-y: auto;
|
451 |
+
line-height: 1.8;
|
452 |
white-space: pre-wrap;
|
453 |
+
transition: all 0.3s ease;
|
454 |
+
}
|
455 |
+
|
456 |
+
.large-output-box:hover {
|
457 |
+
transform: translateY(-2px);
|
458 |
+
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
|
459 |
}
|
460 |
|
461 |
/* 탭 내비게이션 스타일 */
|
462 |
.tab-nav {
|
463 |
background: rgba(255, 255, 255, 0.9);
|
464 |
+
border-radius: 15px;
|
465 |
+
padding: 15px;
|
466 |
+
margin-bottom: 25px;
|
467 |
+
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
468 |
}
|
469 |
|
470 |
/* 마크다운 텍스트 스타일 */
|
|
|
472 |
text-align: center;
|
473 |
color: #2d3748;
|
474 |
text-shadow: 1px 1px 2px rgba(0,0,0,0.1);
|
475 |
+
font-weight: 500;
|
476 |
+
margin: 20px 0;
|
477 |
}
|
478 |
|
479 |
/* 신년운세 특별 스타일 */
|
480 |
.newyear-container {
|
481 |
background: linear-gradient(135deg, #ffd1d1, #ffecd1);
|
482 |
+
padding: 35px;
|
483 |
border-radius: 20px;
|
484 |
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
|
485 |
+
margin: 25px 0;
|
486 |
+
transition: all 0.3s ease;
|
487 |
+
}
|
488 |
+
|
489 |
+
.newyear-container:hover {
|
490 |
+
transform: translateY(-2px);
|
491 |
+
box-shadow: 0 15px 50px rgba(0, 0, 0, 0.15);
|
492 |
}
|
493 |
|
494 |
.newyear-title {
|
495 |
+
font-size: 26px;
|
496 |
color: #2d3748;
|
497 |
text-align: center;
|
498 |
+
margin-bottom: 25px;
|
499 |
text-shadow: 1px 1px 2px rgba(0,0,0,0.1);
|
500 |
+
font-weight: bold;
|
501 |
}
|
502 |
|
503 |
/* 탭 컨텐츠 스타일 */
|
504 |
.tab-content {
|
505 |
min-height: 600px;
|
506 |
+
padding: 25px;
|
507 |
+
background: rgba(255, 255, 255, 0.02);
|
508 |
+
border-radius: 15px;
|
509 |
+
backdrop-filter: blur(10px);
|
510 |
}
|
|
|
511 |
|
512 |
+
/* 입력 필드 스타일 */
|
513 |
+
input, select, textarea {
|
514 |
+
border-radius: 8px !important;
|
515 |
+
border: 1px solid rgba(0, 0, 0, 0.1) !important;
|
516 |
+
padding: 12px !important;
|
517 |
+
transition: all 0.3s ease !important;
|
518 |
+
}
|
519 |
+
|
520 |
+
input:focus, select:focus, textarea:focus {
|
521 |
+
border-color: #6e8efb !important;
|
522 |
+
box-shadow: 0 0 0 2px rgba(110, 142, 251, 0.2) !important;
|
523 |
+
transform: translateY(-1px);
|
524 |
+
}
|
525 |
+
"""
|
526 |
|
527 |
+
# Gradio UI 부분
|
528 |
with gr.Blocks(css=css) as demo:
|
529 |
gr.Markdown("# 🔮 AI 사주 운세 분석 시스템 ✨", elem_id="title")
|
530 |
gr.Markdown("### 🌟 양력 생년월일시와 출생 지역을 입력하시면 AI가 당신의 운세를 분석해드립니다! 💫", elem_id="description1")
|
|
|
539 |
label="⏰ 생시 (예: 1030)",
|
540 |
placeholder="24시간 형식으로 입력 (예: 2230)"
|
541 |
)
|
542 |
+
timezone_slider = gr.Slider(
|
543 |
+
minimum=-12,
|
544 |
+
maximum=14,
|
545 |
+
value=9,
|
546 |
+
step=1,
|
547 |
+
label="🌍 출생 지역 시간대 (UTC)",
|
548 |
+
info="서울은 UTC+9 입니다. 출생 지역의 UTC 시간대를 선택하세요.",
|
549 |
+
elem_classes="timezone-slider"
|
550 |
+
)
|
551 |
+
gr.Markdown(
|
552 |
+
"현재 선택된 시간대: " + TIMEZONE_MAPPING[9],
|
553 |
+
elem_id="selected-timezone-display"
|
554 |
)
|
555 |
|
556 |
with gr.Group(elem_classes="output-container"):
|
|
|
559 |
lunIljin_state = gr.State()
|
560 |
|
561 |
get_lunar_info_btn = gr.Button("✨ 운세 분석하기")
|
562 |
+
|
563 |
+
|
564 |
+
|
565 |
+
|
566 |
|
567 |
with gr.Tab("🧐 성격과 특징 분석"):
|
568 |
with gr.Group(elem_classes="output-container"):
|
|
|
587 |
)
|
588 |
analyze_button2 = gr.Button("🎯 2025년 운세 보기", size="large")
|
589 |
|
590 |
+
|
591 |
+
|
592 |
+
|
593 |
# 이벤트 연결
|
594 |
get_lunar_info_btn.click(
|
595 |
fn=get_lunar_info,
|
596 |
+
inputs=[solYear, solMonth, solDay, solHour, timezone_slider],
|
597 |
outputs=[output1, lunIljin_state, analysis_result]
|
598 |
)
|
599 |
+
|
600 |
+
|
601 |
analyze_button.click(
|
602 |
fn=analyze_character,
|
603 |
inputs=[lunIljin_state],
|
|
|
614 |
demo.launch()
|
615 |
|
616 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|