Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -477,4 +477,308 @@ def create_saju_table(saju_data: Dict) -> str:
|
|
477 |
<thead>
|
478 |
<tr style='background: linear-gradient(135deg, #74b9ff, #0984e3); color: white;'>
|
479 |
<th style='padding: 15px; border: 1px solid #ddd; font-size: 16px;'>년주 (祖上)</th>
|
480 |
-
<th style='padding: 15px; border: 1px solid #ddd; font-size: 16px;'>월주 (父母)</th>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
477 |
<thead>
|
478 |
<tr style='background: linear-gradient(135deg, #74b9ff, #0984e3); color: white;'>
|
479 |
<th style='padding: 15px; border: 1px solid #ddd; font-size: 16px;'>년주 (祖上)</th>
|
480 |
+
<th style='padding: 15px; border: 1px solid #ddd; font-size: 16px;'>월주 (父母)</th>
|
481 |
+
<th style='padding: 15px; border: 1px solid #ddd; font-size: 16px;'>일주 (本人)</th>
|
482 |
+
<th style='padding: 15px; border: 1px solid #ddd; font-size: 16px;'>시주 (子女)</th>
|
483 |
+
</tr>
|
484 |
+
</thead>
|
485 |
+
<tbody>
|
486 |
+
<tr style='text-align: center; font-size: 20px; font-weight: bold;'>
|
487 |
+
<td style='padding: 20px; border: 1px solid #ddd; background: linear-gradient(135deg, #ffb3ba, #ff7675);'>{saju['year']}</td>
|
488 |
+
<td style='padding: 20px; border: 1px solid #ddd; background: linear-gradient(135deg, #bae1ff, #74b9ff);'>{saju['month']}</td>
|
489 |
+
<td style='padding: 20px; border: 1px solid #ddd; background: linear-gradient(135deg, #ffffba, #fdcb6e); border: 3px solid #f39c12;'>{saju['day']}</td>
|
490 |
+
<td style='padding: 20px; border: 1px solid #ddd; background: linear-gradient(135deg, #baffc9, #00b894);'>{saju['time']}</td>
|
491 |
+
</tr>
|
492 |
+
</tbody>
|
493 |
+
</table>
|
494 |
+
<p style='text-align: center; color: #7f8c8d; margin-bottom: 0;'>
|
495 |
+
<strong>일간:</strong> {detailed['day_master']} ({detailed['day_master_element']}) |
|
496 |
+
<strong>계절:</strong> {detailed['month_season']} |
|
497 |
+
<strong>강약:</strong> {detailed['strength']}
|
498 |
+
</p>
|
499 |
+
</div>
|
500 |
+
|
501 |
+
<div style='background: linear-gradient(135deg, #a29bfe 0%, #6c5ce7 100%); color: white; padding: 20px; border-radius: 12px; margin-bottom: 25px;'>
|
502 |
+
<h3 style='margin-top: 0; text-align: center;'>🌟 오행 분석</h3>
|
503 |
+
<div style='display: grid; grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); gap: 15px; margin-bottom: 15px;'>"""
|
504 |
+
|
505 |
+
for element, count in elements['count'].items():
|
506 |
+
percentage = elements['percentage'].get(element, 0)
|
507 |
+
color_map = {'목': '#00b894', '화': '#e17055', '토': '#fdcb6e', '금': '#74b9ff', '수': '#6c5ce7'}
|
508 |
+
color = color_map.get(element, '#ddd')
|
509 |
+
|
510 |
+
table_html += f"""
|
511 |
+
<div style='background-color: rgba(255,255,255,0.2); padding: 12px; border-radius: 8px; text-align: center; backdrop-filter: blur(10px);'>
|
512 |
+
<div style='color: {color}; font-size: 18px; font-weight: bold;'>{element}</div>
|
513 |
+
<div style='font-size: 16px; margin: 5px 0;'>{count}개</div>
|
514 |
+
<div style='font-size: 12px;'>{percentage}%</div>
|
515 |
+
</div>"""
|
516 |
+
|
517 |
+
table_html += f"""
|
518 |
+
</div>
|
519 |
+
<div style='text-align: center; background-color: rgba(255,255,255,0.1); padding: 15px; border-radius: 8px;'>
|
520 |
+
<p style='margin: 5px 0;'><strong>🔥 우세 오행:</strong> {elements['dominant']}</p>
|
521 |
+
<p style='margin: 5px 0;'><strong>💧 부족 오행:</strong> {elements.get('weak', '균형적')}</p>
|
522 |
+
</div>
|
523 |
+
</div>
|
524 |
+
|
525 |
+
<div style='background-color: #fff; padding: 20px; border-radius: 12px; border: 2px solid #e74c3c; box-shadow: 0 4px 15px rgba(231,76,60,0.1);'>
|
526 |
+
<h3 style='color: #e74c3c; text-align: center; margin-bottom: 15px;'>⚡ 사주 구조 분석</h3>
|
527 |
+
<div style='display: grid; grid-template-columns: 1fr 1fr; gap: 20px;'>
|
528 |
+
<div style='background-color: #ffeaa7; padding: 15px; border-radius: 8px;'>
|
529 |
+
<h4 style='color: #2d3436; margin-top: 0;'>🎭 십성 구조</h4>
|
530 |
+
<p style='margin: 5px 0; color: #636e72;'>비견: {detailed['sipseong']['비견']}개</p>
|
531 |
+
<p style='margin: 5px 0; color: #636e72;'>식신: {detailed['sipseong']['식신']}개</p>
|
532 |
+
<p style='margin: 5px 0; color: #636e72;'>재성: {detailed['sipseong']['편재'] + detailed['sipseong']['정재']}개</p>
|
533 |
+
</div>
|
534 |
+
<div style='background-color: #81ecec; padding: 15px; border-radius: 8px;'>
|
535 |
+
<h4 style='color: #2d3436; margin-top: 0;'>🌊 오행 균형도</h4>
|
536 |
+
<p style='margin: 5px 0; color: #636e72;'>목: {elements['percentage'].get('목', 0)}%</p>
|
537 |
+
<p style='margin: 5px 0; color: #636e72;'>화: {elements['percentage'].get('화', 0)}%</p>
|
538 |
+
<p style='margin: 5px 0; color: #636e72;'>토: {elements['percentage'].get('토', 0)}%</p>
|
539 |
+
<p style='margin: 5px 0; color: #636e72;'>금: {elements['percentage'].get('금', 0)}%</p>
|
540 |
+
<p style='margin: 5px 0; color: #636e72;'>수: {elements['percentage'].get('수', 0)}%</p>
|
541 |
+
</div>
|
542 |
+
</div>
|
543 |
+
</div>
|
544 |
+
</div>
|
545 |
+
"""
|
546 |
+
|
547 |
+
return table_html
|
548 |
+
|
549 |
+
def process_saju(name: str, birth_year: int, birth_month: int, birth_day: int,
|
550 |
+
birth_hour: int, birth_minute: int, gender: str, location: str,
|
551 |
+
calendar_type: str, is_intercalation: bool = False) -> Tuple[str, str]:
|
552 |
+
"""사주 처리 메인 함수"""
|
553 |
+
try:
|
554 |
+
if not name.strip():
|
555 |
+
return "이름을 입력해주세요.", ""
|
556 |
+
|
557 |
+
if birth_year < 1000 or birth_year > 2050:
|
558 |
+
return "지원하는 연도 범위는 1000년~2050년입니다.", ""
|
559 |
+
|
560 |
+
if not (1 <= birth_month <= 12):
|
561 |
+
return "올바른 월을 입력해주세요 (1-12).", ""
|
562 |
+
|
563 |
+
if not (1 <= birth_day <= 31):
|
564 |
+
return "올바른 일을 입력해주세요 (1-31).", ""
|
565 |
+
|
566 |
+
if not (0 <= birth_hour <= 23):
|
567 |
+
return "올바른 시간을 입력해주세요 (0-23).", ""
|
568 |
+
|
569 |
+
calculator = SajuCalculator()
|
570 |
+
is_lunar = (calendar_type == "음력")
|
571 |
+
|
572 |
+
saju_data = calculator.calculate_saju(
|
573 |
+
name, birth_year, birth_month, birth_day,
|
574 |
+
birth_hour, birth_minute, gender, location, is_lunar, is_intercalation
|
575 |
+
)
|
576 |
+
|
577 |
+
# 사주 표 생성
|
578 |
+
saju_table = create_saju_table(saju_data)
|
579 |
+
|
580 |
+
# AI 해석 생성
|
581 |
+
ai_interpretation = get_ai_interpretation(saju_data)
|
582 |
+
|
583 |
+
return saju_table, ai_interpretation
|
584 |
+
|
585 |
+
except Exception as e:
|
586 |
+
error_msg = f"❌ 오류가 발생했습니다: {str(e)}"
|
587 |
+
return error_msg, error_msg
|
588 |
+
|
589 |
+
# Gradio 인터페이스 생성
|
590 |
+
def create_interface():
|
591 |
+
with gr.Blocks(
|
592 |
+
title="🔮 한국 전통 사주명리학 시스템",
|
593 |
+
theme=gr.themes.Soft(),
|
594 |
+
css="""
|
595 |
+
.gradio-container {
|
596 |
+
font-family: "Noto Sans KR", Arial, sans-serif !important;
|
597 |
+
}
|
598 |
+
.main-header {
|
599 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
600 |
+
color: white;
|
601 |
+
padding: 30px;
|
602 |
+
border-radius: 15px;
|
603 |
+
text-align: center;
|
604 |
+
margin-bottom: 30px;
|
605 |
+
}
|
606 |
+
"""
|
607 |
+
) as interface:
|
608 |
+
|
609 |
+
gr.HTML("""
|
610 |
+
<div class="main-header">
|
611 |
+
<h1 style="margin: 0; font-size: 2.5em;">🔮 한국 전통 사주명리학 시스템</h1>
|
612 |
+
<p style="margin: 10px 0 0 0; font-size: 1.2em; opacity: 0.9;">
|
613 |
+
한국천문연구원 기준 정밀 음양력 변환 | AI 기반 전문가 해석
|
614 |
+
</p>
|
615 |
+
</div>
|
616 |
+
""")
|
617 |
+
|
618 |
+
with gr.Row():
|
619 |
+
with gr.Column(scale=1):
|
620 |
+
with gr.Group():
|
621 |
+
gr.Markdown("### 📝 개인정보")
|
622 |
+
name = gr.Textbox(
|
623 |
+
label="이름",
|
624 |
+
placeholder="홍길동",
|
625 |
+
info="정확한 이름을 입력해주세요"
|
626 |
+
)
|
627 |
+
|
628 |
+
with gr.Row():
|
629 |
+
gender = gr.Radio(
|
630 |
+
choices=["남자", "여자"],
|
631 |
+
label="성별",
|
632 |
+
value="남자"
|
633 |
+
)
|
634 |
+
location = gr.Textbox(
|
635 |
+
label="출생지",
|
636 |
+
placeholder="서울특별시",
|
637 |
+
info="태어난 도시를 입력해주세요"
|
638 |
+
)
|
639 |
+
|
640 |
+
with gr.Group():
|
641 |
+
gr.Markdown("### 📅 생년월일시")
|
642 |
+
calendar_type = gr.Radio(
|
643 |
+
choices=["양력", "음력"],
|
644 |
+
label="달력 구분",
|
645 |
+
value="양력",
|
646 |
+
info="양력/음력을 선택해주세요"
|
647 |
+
)
|
648 |
+
|
649 |
+
with gr.Row():
|
650 |
+
birth_year = gr.Number(
|
651 |
+
label="년도",
|
652 |
+
value=1990,
|
653 |
+
precision=0,
|
654 |
+
info="1000-2050년 지원"
|
655 |
+
)
|
656 |
+
birth_month = gr.Number(
|
657 |
+
label="월",
|
658 |
+
value=1,
|
659 |
+
precision=0,
|
660 |
+
minimum=1,
|
661 |
+
maximum=12
|
662 |
+
)
|
663 |
+
birth_day = gr.Number(
|
664 |
+
label="일",
|
665 |
+
value=1,
|
666 |
+
precision=0,
|
667 |
+
minimum=1,
|
668 |
+
maximum=31
|
669 |
+
)
|
670 |
+
|
671 |
+
with gr.Row():
|
672 |
+
birth_hour = gr.Number(
|
673 |
+
label="시 (24시간제)",
|
674 |
+
value=0,
|
675 |
+
precision=0,
|
676 |
+
minimum=0,
|
677 |
+
maximum=23,
|
678 |
+
info="정확한 출생시간이 중요합니다"
|
679 |
+
)
|
680 |
+
birth_minute = gr.Number(
|
681 |
+
label="분",
|
682 |
+
value=0,
|
683 |
+
precision=0,
|
684 |
+
minimum=0,
|
685 |
+
maximum=59
|
686 |
+
)
|
687 |
+
|
688 |
+
is_intercalation = gr.Checkbox(
|
689 |
+
label="윤달 여부 (음력인 경우만)",
|
690 |
+
value=False,
|
691 |
+
info="음력인 경우 윤달인지 확인해주세요",
|
692 |
+
visible=False
|
693 |
+
)
|
694 |
+
|
695 |
+
submit_btn = gr.Button(
|
696 |
+
"🔮 정밀 사주 분석 시작",
|
697 |
+
variant="primary",
|
698 |
+
size="lg",
|
699 |
+
elem_classes="submit-button"
|
700 |
+
)
|
701 |
+
|
702 |
+
gr.Markdown("""
|
703 |
+
---
|
704 |
+
### 💡 사용 팁
|
705 |
+
- **정확한 출생시간**: 2시간 차이로 시주가 바뀝니다
|
706 |
+
- **음력 주의**: 윤달 여부를 정확히 확인해주세요
|
707 |
+
- **절기 기준**: 명리학은 24절기로 월을 구분합니다
|
708 |
+
""")
|
709 |
+
|
710 |
+
with gr.Column(scale=2):
|
711 |
+
with gr.Tab("📊 사주명식"):
|
712 |
+
saju_output = gr.HTML(
|
713 |
+
label="사주 분석 결과",
|
714 |
+
elem_classes="saju-result"
|
715 |
+
)
|
716 |
+
|
717 |
+
with gr.Tab("🎯 AI 전문가 해석"):
|
718 |
+
interpretation_output = gr.Textbox(
|
719 |
+
label="상세 운세 해석",
|
720 |
+
lines=25,
|
721 |
+
placeholder="사주 해석 결과가 여기에 표시됩니다...",
|
722 |
+
show_copy_button=True,
|
723 |
+
elem_classes="interpretation-result"
|
724 |
+
)
|
725 |
+
|
726 |
+
# 이벤트 처리
|
727 |
+
def toggle_intercalation(calendar_type):
|
728 |
+
return gr.update(visible=(calendar_type == "음력"))
|
729 |
+
|
730 |
+
calendar_type.change(
|
731 |
+
fn=toggle_intercalation,
|
732 |
+
inputs=[calendar_type],
|
733 |
+
outputs=[is_intercalation]
|
734 |
+
)
|
735 |
+
|
736 |
+
submit_btn.click(
|
737 |
+
fn=process_saju,
|
738 |
+
inputs=[name, birth_year, birth_month, birth_day, birth_hour,
|
739 |
+
birth_minute, gender, location, calendar_type, is_intercalation],
|
740 |
+
outputs=[saju_output, interpretation_output]
|
741 |
+
)
|
742 |
+
|
743 |
+
gr.HTML("""
|
744 |
+
<div style="background-color: #f8f9fa; padding: 20px; border-radius: 10px; margin-top: 30px;">
|
745 |
+
<h3 style="color: #495057; text-align: center;">📌 시스템 특징</h3>
|
746 |
+
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-top: 20px;">
|
747 |
+
<div style="text-align: center; padding: 15px;">
|
748 |
+
<div style="font-size: 24px; margin-bottom: 10px;">🎯</div>
|
749 |
+
<strong>한국천문연구원 기준</strong><br>
|
750 |
+
<small>정확한 음양력 변환</small>
|
751 |
+
</div>
|
752 |
+
<div style="text-align: center; padding: 15px;">
|
753 |
+
<div style="font-size: 24px; margin-bottom: 10px;">🤖</div>
|
754 |
+
<strong>AI 전문가 해석</strong><br>
|
755 |
+
<small>GPT-4 기반 상세 분석</small>
|
756 |
+
</div>
|
757 |
+
<div style="text-align: center; padding: 15px;">
|
758 |
+
<div style="font-size: 24px; margin-bottom: 10px;">🌿</div>
|
759 |
+
<strong>24절기 정확 반영</strong><br>
|
760 |
+
<small>전통 명리학 방식</small>
|
761 |
+
</div>
|
762 |
+
<div style="text-align: center; padding: 15px;">
|
763 |
+
<div style="font-size: 24px; margin-bottom: 10px;">📊</div>
|
764 |
+
<strong>상세 오행 분석</strong><br>
|
765 |
+
<small>십성과 강약 판단</small>
|
766 |
+
</div>
|
767 |
+
</div>
|
768 |
+
<p style="text-align: center; margin-top: 20px; color: #6c757d; font-style: italic;">
|
769 |
+
💡 사주는 참고용으로만 활용하시고, 중요한 결정은 신중히 하시기 바랍니다.
|
770 |
+
</p>
|
771 |
+
</div>
|
772 |
+
""")
|
773 |
+
|
774 |
+
return interface
|
775 |
+
|
776 |
+
# 메인 실행
|
777 |
+
if __name__ == "__main__":
|
778 |
+
interface = create_interface()
|
779 |
+
interface.launch(
|
780 |
+
server_name="0.0.0.0",
|
781 |
+
server_port=7860,
|
782 |
+
share=True,
|
783 |
+
show_error=True
|
784 |
+
)
|