saju01 / app.py
aliceblue11's picture
Update app.py
a76e273 verified
raw
history blame
21.3 kB
import gradio as gr
import datetime
import re
class AdvancedSajuCalculator:
def __init__(self):
# 천간 (하늘줄기)
self.heavenly_stems = ['갑', '을', '병', '정', '무', '기', '경', '신', '임', '계']
# 지지 (땅가지)
self.earthly_branches = ['자', '축', '인', '묘', '진', '사', '오', '미', '신', '유', '술', '해']
# 오행
self.five_elements = {
'갑': '목', '을': '목', '병': '화', '정': '화', '무': '토',
'기': '토', '경': '금', '신': '금', '임': '수', '계': '수',
'자': '수', '축': '토', '인': '목', '묘': '목', '진': '토',
'사': '화', '오': '화', '미': '토', '신': '금', '유': '금',
'술': '토', '해': '수'
}
# 오행별 음양
self.element_yin_yang = {
'갑': '양', '을': '음', '병': '양', '정': '음', '무': '양',
'기': '음', '경': '양', '신': '음', '임': '양', '계': '음',
'자': '양', '축': '음', '인': '양', '묘': '음', '진': '양',
'사': '음', '오': '양', '미': '음', '신': '양', '유': '음',
'술': '양', '해': '음'
}
# 십신 (더 정확한 계산)
self.ten_gods = {
('갑', '갑'): '비견', ('갑', '을'): '겁재', ('갑', '병'): '식신', ('갑', '정'): '상관',
('갑', '무'): '편재', ('갑', '기'): '정재', ('갑', '경'): '편관', ('갑', '신'): '정관',
('갑', '임'): '편인', ('갑', '계'): '정인',
('을', '갑'): '겁재', ('을', '을'): '비견', ('을', '병'): '상관', ('을', '정'): '식신',
('을', '무'): '정재', ('을', '기'): '편재', ('을', '경'): '정관', ('을', '신'): '편관',
('을', '임'): '정인', ('을', '계'): '편인'
}
# 24절기 (양력 기준 근사값)
self.solar_terms = [
(2, 4, '입춘'), (2, 19, '우수'), (3, 6, '경칩'), (3, 21, '춘분'),
(4, 5, '청명'), (4, 20, '곡우'), (5, 6, '입하'), (5, 21, '소만'),
(6, 6, '망종'), (6, 21, '하지'), (7, 7, '소서'), (7, 23, '대서'),
(8, 8, '입추'), (8, 23, '처서'), (9, 8, '백로'), (9, 23, '추분'),
(10, 8, '한로'), (10, 23, '상강'), (11, 7, '입동'), (11, 22, '소설'),
(12, 7, '대설'), (12, 22, '동지'), (1, 6, '소한'), (1, 20, '대한')
]
# 지역별 시간 보정 (서울 기준 분 단위)
self.location_offsets = {
'서울': 0, '서울특별시': 0, '경기': 0, '경기도': 0, '인천': 0,
'강원': +12, '강원도': +12, '춘천': +12, '강릉': +20, '원주': +8,
'충북': -8, '충청북도': -8, '청주': -8, '충주': 0,
'충남': -16, '충청남도': -16, '대전': -12, '천안': -12,
'전북': -20, '전라북도': -20, '전주': -20, '군산': -24,
'전남': -24, '전라남도': -24, '광주': -20, '목포': -32, '여수': -16,
'경북': +8, '경상북도': +8, '대구': +4, '포항': +20, '경주': +16,
'경남': -4, '경상남도': -4, '부산': +12, '울산': +16, '창원': +4, '진주': -8,
'제주': -20, '제주도': -20, '제주시': -20, '서귀포': -20
}
def get_location_offset(self, location):
"""출생지별 시간 보정"""
if not location:
return 0
location = location.strip()
for key, offset in self.location_offsets.items():
if key in location or location in key:
return offset
return 0
def get_solar_term_month(self, year, month, day):
"""절기를 고려한 정확한 월주 계산"""
# 입춘 기준으로 년도 결정
if month == 1 or month == 2:
# 입춘(2/4) 이전이면 전년도
lichun_date = datetime.date(year, 2, 4)
current_date = datetime.date(year, month, day)
if current_date < lichun_date:
year -= 1
# 절기 기준 월 계산
month_mapping = {
(2, 4): 1, # 입춘~경칩 = 정월
(3, 6): 2, # 경칩~청명 = 2월
(4, 5): 3, # 청명~입하 = 3월
(5, 6): 4, # 입하~망종 = 4월
(6, 6): 5, # 망종~소서 = 5월
(7, 7): 6, # 소서~입추 = 6월
(8, 8): 7, # 입추~백로 = 7월
(9, 8): 8, # 백로~한로 = 8월
(10, 8): 9, # 한로~입동 = 9월
(11, 7): 10, # 입동~대설 = 10월
(12, 7): 11, # 대설~소한 = 11월
(1, 6): 12 # 소한~입춘 = 12월
}
# 현재 날짜가 어느 절기 구간에 속하는지 확인
current_date = datetime.date(year if month >= 2 else year + 1, month, day)
for (term_month, term_day), lunar_month in month_mapping.items():
term_date = datetime.date(year if term_month >= 2 else year + 1, term_month, term_day)
if current_date >= term_date:
solar_month = lunar_month
return year, solar_month
def parse_date(self, date_str):
"""날짜 파싱"""
numbers = re.findall(r'\d+', str(date_str))
if not numbers:
raise ValueError("날짜에서 숫자를 찾을 수 없습니다")
if len(numbers) == 1:
num_str = numbers[0]
if len(num_str) == 8: # YYYYMMDD
year = int(num_str[:4])
month = int(num_str[4:6])
day = int(num_str[6:8])
elif len(num_str) == 6: # YYMMDD
year_part = int(num_str[:2])
year = 1900 + year_part if year_part > 30 else 2000 + year_part
month = int(num_str[2:4])
day = int(num_str[4:6])
else:
raise ValueError(f"날짜 형식을 인식할 수 없습니다: {num_str}")
elif len(numbers) >= 3:
year = int(numbers[0])
month = int(numbers[1])
day = int(numbers[2])
if year < 100:
year = 1900 + year if year > 30 else 2000 + year
else:
raise ValueError("날짜 정보가 부족합니다")
return year, month, day
def parse_time(self, time_str):
"""시간 파싱"""
if not time_str:
return 12, 0
numbers = re.findall(r'\d+', str(time_str))
if not numbers:
return 12, 0
if len(numbers) == 1:
time_num = numbers[0]
if len(time_num) == 4: # HHMM
hour = int(time_num[:2])
minute = int(time_num[2:4])
elif len(time_num) == 3: # HMM
hour = int(time_num[0])
minute = int(time_num[1:3])
else: # H or HH
hour = int(time_num)
minute = 0
else:
hour = int(numbers[0])
minute = int(numbers[1]) if len(numbers) > 1 else 0
hour = hour % 24
return hour, minute
def get_ganzhi(self, year, month, day, hour):
"""정확한 간지 계산 (절기 고려)"""
# 절기 기준 연도와 월 계산
adjusted_year, solar_month = self.get_solar_term_month(year, month, day)
# 기준일: 1900년 1월 1일 (경자년 정축월 갑인일)
base_date = datetime.date(1900, 1, 1)
target_date = datetime.date(year, month, day)
days_diff = (target_date - base_date).days
# 연주 계산 (절기 기준)
year_cycle = (adjusted_year - 1900) % 60
year_stem_index = year_cycle % 10
year_branch_index = year_cycle % 12
# 월주 계산 (절기 기준)
month_stem_index = (year_stem_index * 2 + solar_month - 1) % 10
month_branch_index = (solar_month + 1) % 12
# 일주 계산
day_cycle = (days_diff + 10) % 60 # 1900.1.1이 갑인일
day_stem_index = day_cycle % 10
day_branch_index = day_cycle % 12
# 시주 계산
hour_branch_index = ((hour + 1) // 2) % 12
hour_stem_index = (day_stem_index * 2 + hour_branch_index) % 10
return {
'year': (self.heavenly_stems[year_stem_index], self.earthly_branches[year_branch_index]),
'month': (self.heavenly_stems[month_stem_index], self.earthly_branches[month_branch_index]),
'day': (self.heavenly_stems[day_stem_index], self.earthly_branches[day_branch_index]),
'hour': (self.heavenly_stems[hour_stem_index], self.earthly_branches[hour_branch_index])
}
def analyze_elements(self, ganzhi):
"""오행 분석"""
elements = []
for pillar in ganzhi.values():
stem_element = self.five_elements[pillar[0]]
branch_element = self.five_elements[pillar[1]]
elements.extend([stem_element, branch_element])
element_count = {}
for element in ['목', '화', '토', '금', '수']:
element_count[element] = elements.count(element)
return element_count
def get_ten_gods_analysis(self, ganzhi):
"""십신 분석 (정확한 계산)"""
day_stem = ganzhi['day'][0]
analysis = {}
for pillar_name, pillar in ganzhi.items():
stem = pillar[0]
branch = pillar[1]
# 천간 십신
if (day_stem, stem) in self.ten_gods:
stem_relation = self.ten_gods[(day_stem, stem)]
else:
# 기본 오행 관계로 계산
day_element = self.five_elements[day_stem]
stem_element = self.five_elements[stem]
stem_relation = self.get_basic_relation(day_element, stem_element)
# 지지 십신 (지지의 본기로 계산)
branch_element = self.five_elements[branch]
day_element = self.five_elements[day_stem]
branch_relation = self.get_basic_relation(day_element, branch_element)
analysis[pillar_name] = {
'stem_relation': stem_relation,
'branch_relation': branch_relation
}
return analysis
def get_basic_relation(self, day_element, target_element):
"""기본 오행 관계 계산"""
relations = {
'목': {'목': '비견', '화': '식신', '토': '편재', '금': '편관', '수': '편인'},
'화': {'목': '편인', '화': '비견', '토': '식신', '금': '편재', '수': '편관'},
'토': {'목': '편관', '화': '편인', '토': '비견', '금': '식신', '수': '편재'},
'금': {'목': '편재', '화': '편관', '토': '편인', '금': '비견', '수': '식신'},
'수': {'목': '식신', '화': '편재', '토': '편관', '금': '편인', '수': '비견'}
}
return relations[day_element][target_element]
def format_saju_result(calculator, ganzhi, elements, ten_gods, birth_info):
"""사주 결과 포맷팅 - 전문적인 표 형태"""
# 오행별 색상 및 이모지
element_info = {
'목': {'color': '#28a745', 'emoji': '🌳', 'name': '목(木)'},
'화': {'color': '#dc3545', 'emoji': '🔥', 'name': '화(火)'},
'토': {'color': '#ffc107', 'emoji': '🏔️', 'name': '토(土)'},
'금': {'color': '#6c757d', 'emoji': '⚔️', 'name': '금(金)'},
'수': {'color': '#007bff', 'emoji': '💧', 'name': '수(水)'}
}
def get_colored_char(char, element):
info = element_info[element]
return f'<span style="color: {info["color"]}; font-weight: bold; font-size: 18px;">{char}</span>'
def get_element_strength(count):
if count >= 3:
return '<span style="color: #dc3545;">**강함**</span>'
elif count == 2:
return '<span style="color: #ffc107;">**보통**</span>'
else:
return '<span style="color: #6c757d;">**약함**</span>'
# 시간 보정 정보
time_info = ""
if birth_info['location_offset'] != 0:
sign = "+" if birth_info['location_offset'] > 0 else ""
time_info = f"""
### ⏰ 출생지 시간 보정
- **입력 시간**: {birth_info['original_time']}
- **보정 시간**: {birth_info['corrected_time']}
- **보정값**: {birth_info['birth_place']} 기준 {sign}{birth_info['location_offset']}
"""
# 사주 표 생성
year_stem = get_colored_char(ganzhi['year'][0], calculator.five_elements[ganzhi['year'][0]])
year_branch = get_colored_char(ganzhi['year'][1], calculator.five_elements[ganzhi['year'][1]])
month_stem = get_colored_char(ganzhi['month'][0], calculator.five_elements[ganzhi['month'][0]])
month_branch = get_colored_char(ganzhi['month'][1], calculator.five_elements[ganzhi['month'][1]])
day_stem = get_colored_char(ganzhi['day'][0], calculator.five_elements[ganzhi['day'][0]])
day_branch = get_colored_char(ganzhi['day'][1], calculator.five_elements[ganzhi['day'][1]])
hour_stem = get_colored_char(ganzhi['hour'][0], calculator.five_elements[ganzhi['hour'][0]])
hour_branch = get_colored_char(ganzhi['hour'][1], calculator.five_elements[ganzhi['hour'][1]])
result = f"""
# 🔮 사주명리 만세력 완전분석
## 📋 출생 정보
- **생년월일**: {birth_info['birth_datetime'].strftime('%Y년 %m월 %d일')} (양력)
- **출생시간**: {birth_info['corrected_time']}
- **성별**: {birth_info['gender']}
- **출생지**: {birth_info['birth_place']}
{time_info}
## 🏛️ 사주(四柱) 만세력 【절기 기준 정확 계산】
<div style="background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); padding: 20px; border-radius: 10px; margin: 20px 0;">
| **구분** | **시주(時柱)** | **일주(日柱)** | **월주(月柱)** | **연주(年柱)** |
|:--------:|:-------------:|:-------------:|:-------------:|:-------------:|
| **천간** | {hour_stem} | {day_stem} | {month_stem} | {year_stem} |
| **지지** | {hour_branch} | {day_branch} | {month_branch} | {year_branch} |
| **간지** | **{ganzhi['hour'][0]}{ganzhi['hour'][1]}** | **{ganzhi['day'][0]}{ganzhi['day'][1]}** | **{ganzhi['month'][0]}{ganzhi['month'][1]}** | **{ganzhi['year'][0]}{ganzhi['year'][1]}** |
| **의미** | 자식·말년 | 본인·배우자 | 부모·청년 | 조상·유년 |
| **오행** | {element_info[calculator.five_elements[ganzhi['hour'][0]]]['name']} / {element_info[calculator.five_elements[ganzhi['hour'][1]]]['name']} | {element_info[calculator.five_elements[ganzhi['day'][0]]]['name']} / {element_info[calculator.five_elements[ganzhi['day'][1]]]['name']} | {element_info[calculator.five_elements[ganzhi['month'][0]]]['name']} / {element_info[calculator.five_elements[ganzhi['month'][1]]]['name']} | {element_info[calculator.five_elements[ganzhi['year'][0]]]['name']} / {element_info[calculator.five_elements[ganzhi['year'][1]]]['name']} |
</div>
> **일간**: {get_colored_char(ganzhi['day'][0], calculator.five_elements[ganzhi['day'][0]])} - 당신의 본성을 나타내는 핵심 요소
## 🌟 오행(五行) 균형 분석
<div style="background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 15px 0;">
| **오행** | **개수** | **비율** | **강약** | **특성** |
|:--------:|:--------:|:--------:|:--------:|:--------:|
| {element_info['목']['emoji']} **목(木)** | **{elements['목']}개** | {(elements['목']/8*100):.1f}% | {get_element_strength(elements['목'])} | 성장·창조·유연성 |
| {element_info['화']['emoji']} **화(火)** | **{elements['화']}개** | {(elements['화']/8*100):.1f}% | {get_element_strength(elements['화'])} | 열정·활동·표현력 |
| {element_info['토']['emoji']} **토(土)** | **{elements['토']}개** | {(elements['토']/8*100):.1f}% | {get_element_strength(elements['토'])} | 안정·신뢰·포용력 |
| {element_info['금']['emoji']} **금(金)** | **{elements['금']}개** | {(elements['금']/8*100):.1f}% | {get_element_strength(elements['금'])} | 정의·결단·리더십 |
| {element_info['수']['emoji']} **수(水)** | **{elements['수']}개** | {(elements['수']/8*100):.1f}% | {get_element_strength(elements['수'])} | 지혜·적응·사고력 |
</div>
## 🎭 십신(十神) 관계 분석
<div style="background: #fff3cd; padding: 15px; border-radius: 8px; margin: 15px 0;">
| **주(柱)** | **천간 십신** | **지지 십신** | **의미** |
|:----------:|:------------:|:------------:|:--------:|
| **시주** | {ten_gods['hour']['stem_relation']} | {ten_gods['hour']['branch_relation']} | 자식운·말년운 |
| **일주** | {ten_gods['day']['stem_relation']} | {ten_gods['day']['branch_relation']} | 본인·결혼운 |
| **월주** | {ten_gods['month']['stem_relation']} | {ten_gods['month']['branch_relation']} | 부모운·사회운 |
| **연주** | {ten_gods['year']['stem_relation']} | {ten_gods['year']['branch_relation']} | 조상운·초년운 |
</div>
## 💡 종합 운세 해석
### 🎯 일간 분석 (핵심 성격)
일간 **{get_colored_char(ganzhi['day'][0], calculator.five_elements[ganzhi['day'][0]])}**({element_info[calculator.five_elements[ganzhi['day'][0]]]['name']})의 특성:
{get_detailed_personality(calculator.five_elements[ganzhi['day'][0]])}
### ⚖️ 오행 균형 진단
{get_advanced_balance_advice(elements)}
### 🔮 운세 포인트
{get_fortune_points(ganzhi, elements, calculator)}
---
### 📌 분석 기준
- ✅ **절기 기준 계산**: 입춘을 기준으로 정확한 연주·월주 산정
- ✅ **출생지 시간 보정**: 지역별 경도차 반영
- ✅ **전통 명리학**: 정통 사주명리학 이론 적용
### ⚠️ 유의사항
본 분석은 전통 사주명리학을 기반으로 한 기본 해석입니다. 더 정확하고 개인화된 분석을 위해서는 전문가 상담을 권장드립니다.
---
*분석 완료: {datetime.datetime.now().strftime('%Y년 %m월 %d일 %H시 %M분')}*
"""
return result
def get_detailed_personality(element):
"""상세한 성격 분석"""
personalities = {
'목': """
- **성격**: 따뜻하고 인정 많으며, 성장과 발전을 추구하는 성향
- **장점**: 창의적, 유연한 사고, 협력적, 포용력이 강함
- **특징**: 새로운 것을 배우고 성장하려는 의욕이 강하며, 타인을 도우려는 마음이 큼
- **주의점**: 때로는 우유부단하거나 결정을 미루는 경향""",
'화': """
- **성격**: 밝고 활발하며, 열정적이고 사교적인 성향
- **장점**: 리더십, 표현력, 긍정적 에너지, 추진력이 강함
- **특징**: 사람들과 어울리기를 좋아하며, 새로운 도전을 즐김
- **주의점**: 때로는 성급하거나 감정적인 판단을 할 수 있음""",
'토': """
- **성격**: 차분하고 신중하며, 안정을 추구하는 성향
- **장점**: 책임감, 인내력, 신뢰성, 포용력이 뛰어남
- **특징**: 꾸준하고 성실하며, 다른 사람들의 중재자 역할을 잘함
- **주의점**: 때로는 변화에 적응이 늦거나 고집이 셀 수 있음""",
'금': """
- **성격**: 원칙적이고 정의로우며, 강한 의지력을 가진 성향
- **장점**: 결단력, 리더십, 정직함, 목표 지향적
- **특징**: 옳고 그름이 분명하며, 자신의 신념을 관철시키려 함
- **주의점**: 때로는 융통성이 부족하거나 타인에게 엄격할 수 있음""",
'수': """
- **성격**: 지혜롭고 사려 깊으며, 깊이 있는 사고를 하는 성향
- **장점**: 통찰력, 적응력, 학습능력, 직관력이 뛰어남
- **특징**: 상황을 정확히 파악하고 현명한 판단을 내리는 능력
- **주의점**: 때로는 지나치게 신중하거나 소극적일 수 있음"""
}
return personalities.get(element, "균형 잡힌 성격을 가지고 있습니다.")
def get_advanced_balance_advice(elements):
"""고급 오행 균형 조언"""
max_element = max(elements, key=elements.get)
min_element = min(elements, key=elements.get)
max_count = elements[max_element]
min_count = elements[min_element]
advice = f"""
**🔸 강한 오행**: {max_element} ({max_count}개) - 이 기운이 강하게 나타남
**🔸 약한 오행**: {min_element} ({min_count}개) - 이 기운을 보강하면 좋음
**💫 균형 조절 방법**:
"""
if max_count - min_count > 2:
advice += f"- {min_element}를 보강하는 색상, 방향, 직업을 선택\n"
advice += f"- {max_element}의 과도한 기운을 조절하는 것이 필요\n"
advice += "- 전체적인 조화를 위한 노력이 중요"
else:
advice += "- 현재 오행이 비교적 균형잡혀 있는 상태\n"
advice += "- 기존의 균형을 유지하면서 발전시키는 것이