aliceblue11 commited on
Commit
a76e273
·
verified ·
1 Parent(s): ff7bc08

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +252 -280
app.py CHANGED
@@ -2,7 +2,7 @@ import gradio as gr
2
  import datetime
3
  import re
4
 
5
- class SajuCalculator:
6
  def __init__(self):
7
  # 천간 (하늘줄기)
8
  self.heavenly_stems = ['갑', '을', '병', '정', '무', '기', '경', '신', '임', '계']
@@ -19,45 +19,93 @@ class SajuCalculator:
19
  '술': '토', '해': '수'
20
  }
21
 
22
- # 십신
 
 
 
 
 
 
 
 
 
23
  self.ten_gods = {
24
- '': {'': '비견/겁재', '': '식신/상관', '': '편재/정재', '': '편관/정관', '': '편인/정인'},
25
- '': {'': '편인/정인', '': '비견/겁재', '': '식신/상관', '': '편재/정재', '': '편관/정관'},
26
- '': {'목': '편관/정관', '': '편인/정인', '': '비견/겁재', '': '식신/상관', '수': '편재/정재'},
27
- '': {'': '편재/정재', '': '편관/정관', '': '편인/정인', '': '비견/겁재', '': '식신/상관'},
28
- '': {'': '식신/상관', '': '편재/정재', '': '편관/정관', '': '편인/정인', '': '비견/겁재'}
 
29
  }
30
 
 
 
 
 
 
 
 
 
 
 
31
  # 지역별 시간 보정 (서울 기준 분 단위)
32
  self.location_offsets = {
33
- '서울': 0, '서울특별시': 0, '경기': 0, '경기도': 0, '인천': 0, '인천광역시': 0,
34
- '강원': +12, '강원도': +12, '춘천': +12, '원주': +8, '강릉': +20,
35
  '충북': -8, '충청북도': -8, '청주': -8, '충주': 0,
36
- '충남': -16, '충청남도': -16, '대전': -12, '대전광역시': -12, '천안': -12,
37
  '전북': -20, '전라북도': -20, '전주': -20, '군산': -24,
38
- '전남': -24, '전라남도': -24, '광주': -20, '광주광역시': -20, '목포': -32, '여수': -16,
39
- '경북': +8, '경상북도': +8, '대구': +4, '대구광역시': +4, '포항': +20, '경주': +16,
40
- '경남': -4, '경상남도': -4, '부산': +12, '부산광역시': +12, '울산': +16, '울산광역시': +16, '창원': +4, '마산': +4, '진주': -8,
41
  '제주': -20, '제주도': -20, '제주시': -20, '서귀포': -20
42
  }
43
 
44
  def get_location_offset(self, location):
45
- """출생지에 따른 시간 보정값 반환 (분 단위)"""
46
  if not location:
47
  return 0
48
-
49
  location = location.strip()
50
-
51
- # 정확한 매칭 시도
52
- if location in self.location_offsets:
53
- return self.location_offsets[location]
54
-
55
- # 부분 매칭 시도
56
  for key, offset in self.location_offsets.items():
57
  if key in location or location in key:
58
  return offset
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
- return 0 # 기본값 (서울 기준)
61
 
62
  def parse_date(self, date_str):
63
  """날짜 파싱"""
@@ -120,23 +168,28 @@ class SajuCalculator:
120
  return hour, minute
121
 
122
  def get_ganzhi(self, year, month, day, hour):
123
- """간지 계산"""
124
- base_date = datetime.datetime(1900, 1, 1)
125
- target_date = datetime.datetime(year, month, day)
126
 
 
 
 
127
  days_diff = (target_date - base_date).days
128
 
129
- # 연주 계산
130
- year_stem_index = (year - 1900 + 6) % 10
131
- year_branch_index = (year - 1900 + 0) % 12
 
132
 
133
- # 월주 계산
134
- month_stem_index = (year_stem_index * 2 + month + 1) % 10
135
- month_branch_index = (month + 1) % 12
136
 
137
  # 일주 계산
138
- day_stem_index = (days_diff + 10) % 10
139
- day_branch_index = (days_diff + 2) % 12
 
140
 
141
  # 시주 계산
142
  hour_branch_index = ((hour + 1) // 2) % 12
@@ -164,17 +217,27 @@ class SajuCalculator:
164
  return element_count
165
 
166
  def get_ten_gods_analysis(self, ganzhi):
167
- """십신 분석"""
168
  day_stem = ganzhi['day'][0]
169
- day_element = self.five_elements[day_stem]
170
 
171
  analysis = {}
172
  for pillar_name, pillar in ganzhi.items():
173
- stem_element = self.five_elements[pillar[0]]
174
- branch_element = self.five_elements[pillar[1]]
 
 
 
 
 
 
 
 
 
175
 
176
- stem_relation = self.ten_gods[day_element][stem_element]
177
- branch_relation = self.ten_gods[day_element][branch_element]
 
 
178
 
179
  analysis[pillar_name] = {
180
  'stem_relation': stem_relation,
@@ -183,288 +246,197 @@ class SajuCalculator:
183
 
184
  return analysis
185
 
186
- def get_element_personality(element):
187
- """오행별 성격 특성"""
188
- personalities = {
189
- '목': "창의적이고 성장 지향적이며, 유연하고 협력적인 성격을 가지고 ���습니다.",
190
- '화': "열정적이고 활동적이며, 밝고 사교적인 성격을 가지고 있습니다.",
191
- '토': "안정적이고 신중하며, 포용력이 있고 책임감이 강한 성격을 가지고 있습니다.",
192
- '금': "원칙적이고 정의로우며, 결단력이 있고 리더십이 강한 성격을 가지고 있습니다.",
193
- '수': "지혜롭고 적응력이 있으며, 깊이 있고 신중한 성격을 가지고 있습니다."
194
- }
195
- return personalities.get(element, "균형 잡힌 성격을 가지고 있습니다.")
196
-
197
- def get_element_balance_advice(elements):
198
- """오행 균형에 따른 조언"""
199
- max_element = max(elements, key=elements.get)
200
- min_element = min(elements, key=elements.get)
201
-
202
- advice = f"현재 {max_element}가 가장 강하고 {min_element}가 가장 약합니다. "
203
-
204
- if elements[max_element] - elements[min_element] > 2:
205
- advice += f"{min_element}를 보강하고 {max_element}의 기운을 조절하는 것이 좋겠습니다."
206
- else:
207
- advice += "전체적으로 균형이 잘 잡혀 있는 편입니다."
208
-
209
- return advice
210
 
211
  def format_saju_result(calculator, ganzhi, elements, ten_gods, birth_info):
212
- """사주 결과 포맷팅 - 표 형태로 출력"""
213
 
214
- # 오행별 색상 설정
215
- element_colors = {
216
- '목': '#28a745', # 녹색
217
- '화': '#dc3545', # 빨강
218
- '토': '#ffc107', # 노랑
219
- '금': '#6c757d', # 회색
220
- '수': '#007bff' # 파랑
221
  }
222
 
223
- def get_colored_element(char, element):
224
- color = element_colors.get(element, '#000000')
225
- return f'<span style="color: {color}; font-weight: bold;">{char}</span>'
226
 
227
- def get_colored_ganzhi(stem, branch):
228
- stem_element = calculator.five_elements[stem]
229
- branch_element = calculator.five_elements[branch]
230
- colored_stem = get_colored_element(stem, stem_element)
231
- colored_branch = get_colored_element(branch, branch_element)
232
- return f'{colored_stem}{colored_branch}'
 
233
 
234
  # 시간 보정 정보
235
- time_correction_info = ""
236
  if birth_info['location_offset'] != 0:
237
  sign = "+" if birth_info['location_offset'] > 0 else ""
238
- time_correction_info = f"""
239
  ### ⏰ 출생지 시간 보정
240
  - **입력 시간**: {birth_info['original_time']}
241
- - **보정 시간**: {birth_info['corrected_time']} ({birth_info['birth_place']} 기준 {sign}{birth_info['location_offset']}분 보정)
 
242
  """
243
 
 
 
 
 
 
 
 
 
 
 
244
  result = f"""
245
- # 🔮 사주명리 만세력 분석결과
246
 
247
- ## 📋 기본정보
248
- - **생년월일**: {birth_info['birth_datetime'].strftime('%Y년 %m월 %d일')}
249
- - **출생시간**: {birth_info['corrected_time']} ({birth_info['birth_place']})
250
  - **성별**: {birth_info['gender']}
251
- {time_correction_info}
252
- ## 🏛️ 사주(四柱) 만세력표
253
 
254
- <div style="text-align: center;">
255
 
256
- | 구분 | **연주(年柱)** | **월주(月柱)** | **일주(日柱)** | **시주(時柱)** |
257
- |:---:|:---:|:---:|:---:|:---:|
258
- | **천간** | {get_colored_element(ganzhi['year'][0], calculator.five_elements[ganzhi['year'][0]])} | {get_colored_element(ganzhi['month'][0], calculator.five_elements[ganzhi['month'][0]])} | {get_colored_element(ganzhi['day'][0], calculator.five_elements[ganzhi['day'][0]])} | {get_colored_element(ganzhi['hour'][0], calculator.five_elements[ganzhi['hour'][0]])} |
259
- | **지지** | {get_colored_element(ganzhi['year'][1], calculator.five_elements[ganzhi['year'][1]])} | {get_colored_element(ganzhi['month'][1], calculator.five_elements[ganzhi['month'][1]])} | {get_colored_element(ganzhi['day'][1], calculator.five_elements[ganzhi['day'][1]])} | {get_colored_element(ganzhi['hour'][1], calculator.five_elements[ganzhi['hour'][1]])} |
260
- | **간지** | **{get_colored_ganzhi(ganzhi['year'][0], ganzhi['year'][1])}** | **{get_colored_ganzhi(ganzhi['month'][0], ganzhi['month'][1])}** | **{get_colored_ganzhi(ganzhi['day'][0], ganzhi['day'][1])}** | **{get_colored_ganzhi(ganzhi['hour'][0], ganzhi['hour'][1])}** |
261
- | **의미** | 조상, 뿌리 | 부모, 성장기 | 본인, 배우자 | 자식, 말년 |
 
 
 
262
 
263
  </div>
264
 
265
- ## 🌟 오행(五行) 분석
266
 
267
- <div style="text-align: center;">
268
 
269
- | 오행 | 개수 | 비율 | 상태 |
270
- |:---:|:---:|:---:|:---:|
271
- | {get_colored_element('목', '목')} | {elements['목']}개 | {(elements['목']/8*100):.1f}% | {'강함' if elements['목'] >= 3 else '보통' if elements['목'] >= 2 else '약함'} |
272
- | {get_colored_element('화', '화')} | {elements['화']}개 | {(elements['화']/8*100):.1f}% | {'강함' if elements['화'] >= 3 else '보통' if elements['화'] >= 2 else '약함'} |
273
- | {get_colored_element('토', '토')} | {elements['토']}개 | {(elements['토']/8*100):.1f}% | {'강함' if elements['토'] >= 3 else '보통' if elements['토'] >= 2 else '약함'} |
274
- | {get_colored_element('금', '금')} | {elements['금']}개 | {(elements['금']/8*100):.1f}% | {'강함' if elements['금'] >= 3 else '보통' if elements['금'] >= 2 else '약함'} |
275
- | {get_colored_element('수', '수')} | {elements['수']}개 | {(elements['수']/8*100):.1f}% | {'강함' if elements['수'] >= 3 else '보통' if elements['수'] >= 2 else '약함'} |
276
 
277
- </div>
 
 
 
 
 
 
278
 
279
- ### 오행 균형 분석
280
- """
281
-
282
- max_element = max(elements, key=elements.get)
283
- min_element = min(elements, key=elements.get)
284
-
285
- result += f"- **가장 강한 오행**: {get_colored_element(max_element, max_element)} ({elements[max_element]}개)\n"
286
- result += f"- **가장 약한 오행**: {get_colored_element(min_element, min_element)} ({elements[min_element]}개)\n"
287
 
288
- result += """
289
- ## 🎭 십신(十神) 분석표
290
 
291
- <div style="text-align: center;">
292
 
293
- | 구분 | **연주** | **월주** | **일주** | **시주** |
294
- |:---:|:---:|:---:|:---:|:---:|
295
- | **천간 십신** | """ + ten_gods['year']['stem_relation'] + """ | """ + ten_gods['month']['stem_relation'] + """ | """ + ten_gods['day']['stem_relation'] + """ | """ + ten_gods['hour']['stem_relation'] + """ |
296
- | **지지 십신** | """ + ten_gods['year']['branch_relation'] + """ | """ + ten_gods['month']['branch_relation'] + """ | """ + ten_gods['day']['branch_relation'] + """ | """ + ten_gods['hour']['branch_relation'] + """ |
 
 
297
 
298
  </div>
299
 
300
- ## 💡 기본 해석
301
 
302
- ### 일간 분석
303
- - **일간**: {get_colored_element(ganzhi['day'][0], calculator.five_elements[ganzhi['day'][0]])} ({calculator.five_elements[ganzhi['day'][0]]})
304
- - **성격**: {get_element_personality(calculator.five_elements[ganzhi['day'][0]])}
305
 
306
- ### 오행 조화
307
- {get_element_balance_advice(elements)}
 
 
 
 
 
308
 
309
  ---
310
 
311
- ### 📌 주의사항
312
- - 분석은 기본적인 사주명리학 원리에 따른 것입니다
313
- - 정확한 해석을 위해서는 전문가 상담을 권장합니다
314
- - 절기, 음력 변환 세부 요소는 고려되지 않았습니다
 
 
 
315
 
316
- *분석 일시: {datetime.datetime.now().strftime('%Y년 %m월 %d일 %H시 %M분')}*
 
317
  """
318
 
319
  return result
320
 
321
- def calculate_saju(birth_date, birth_time, gender, birth_place):
322
- """사주 계산 메인 함수"""
323
- # SajuCalculator 인스턴스 생성
324
- calculator = SajuCalculator()
325
-
326
- try:
327
- # 입력 검증
328
- if not birth_date:
329
- return "❌ 생년월일을 입력해주세요."
330
-
331
- # 날짜 파싱
332
- year, month, day = calculator.parse_date(birth_date)
333
-
334
- # 시간 파싱
335
- hour, minute = calculator.parse_time(birth_time)
336
-
337
- # 출생지에 따른 시간 보정
338
- location_offset = calculator.get_location_offset(birth_place)
339
-
340
- # 보정된 시간 계산
341
- corrected_minute = minute + location_offset
342
- corrected_hour = hour
343
-
344
- # 분이 60을 넘거나 0 미만인 경우 시간 조정
345
- if corrected_minute >= 60:
346
- corrected_hour += corrected_minute // 60
347
- corrected_minute = corrected_minute % 60
348
- elif corrected_minute < 0:
349
- corrected_hour -= (-corrected_minute - 1) // 60 + 1
350
- corrected_minute = 60 + (corrected_minute % 60)
351
-
352
- # 시간이 24를 넘거나 0 미만인 경우 조정
353
- corrected_hour = corrected_hour % 24
354
-
355
- # 날짜 유효성 검사
356
- if year < 1900 or year > 2100:
357
- return f" 연도는 1900~2100 사이여야 합니다. 입력된 연도: {year}"
358
- if month < 1 or month > 12:
359
- return f"❌ 월은 1~12 사이여야 합니다. 입력된 월: {month}"
360
- if day < 1 or day > 31:
361
- return f"❌ 일은 1~31 사이여야 합니다. 입력된 일: {day}"
362
-
363
- # datetime 객체 생성 (보정된 시간 사용)
364
- birth_datetime = datetime.datetime(year, month, day, corrected_hour, corrected_minute)
365
- original_time = f"{hour:02d}:{minute:02d}"
366
- corrected_time = f"{corrected_hour:02d}:{corrected_minute:02d}"
367
-
368
- # 출생 정보 딕셔너리
369
- birth_info = {
370
- 'birth_datetime': birth_datetime,
371
- 'gender': gender,
372
- 'birth_place': birth_place,
373
- 'original_time': original_time,
374
- 'corrected_time': corrected_time,
375
- 'location_offset': location_offset
376
- }
377
-
378
- # 간지 계산
379
- ganzhi = calculator.get_ganzhi(year, month, day, corrected_hour)
380
-
381
- # 오행 분석
382
- elements = calculator.analyze_elements(ganzhi)
383
-
384
- # 십신 분석
385
- ten_gods = calculator.get_ten_gods_analysis(ganzhi)
386
-
387
- # 결과 포맷팅
388
- result = format_saju_result(calculator, ganzhi, elements, ten_gods, birth_info)
389
-
390
- return result
391
-
392
- except ValueError as ve:
393
- return f"❌ 입력 오류: {str(ve)}\n\n💡 입력 예시:\n- 생년월일: 19851015, 1985-10-15\n- 시간: 1430, 14:30"
394
 
395
- except Exception as e:
396
- return f"❌ 계산 오류가 발생했습니다: {str(e)}"
397
-
398
- def create_interface():
399
- """Gradio 인터페이스 생성"""
400
- with gr.Blocks(title="🔮 사주명리 만세력 시스템") as demo:
401
- gr.HTML("""
402
- <div style="text-align: center; padding: 20px;">
403
- <h1>🔮 사주명리학 만세력 분석 시스템</h1>
404
- <p>생년월일시와 출생지 정보를 입력하시면 상세한 만세력을 분석해드립니다.</p>
405
- </div>
406
- """)
407
-
408
- with gr.Row():
409
- with gr.Column(scale=1):
410
- gr.HTML("<h3>📝 정보 입력</h3>")
411
-
412
- birth_date = gr.Textbox(
413
- label="생년월일",
414
- placeholder="19851015 또는 1985-10-15",
415
- info="8자리 숫자 또는 구분자 포함하여 입력"
416
- )
417
-
418
- birth_time = gr.Textbox(
419
- label="태어난 시간 (선택사항)",
420
- placeholder="1430 또는 14:30",
421
- info="4자리 숫자 또는 구분자 포함하여 입력. 비우면 정오로 설정"
422
- )
423
-
424
- gender = gr.Radio(
425
- choices=["남", "여"],
426
- label="성별",
427
- value="남"
428
- )
429
-
430
- birth_place = gr.Textbox(
431
- label="출생지",
432
- placeholder="서울특별시",
433
- info="시/도 단위로 입력해주세요"
434
- )
435
-
436
- calculate_btn = gr.Button(
437
- "🔮 만세력 분석하기",
438
- variant="primary"
439
- )
440
-
441
- with gr.Row():
442
- with gr.Column():
443
- result_output = gr.Markdown(
444
- label="분석 결과",
445
- value="👆 위의 정보를 입력하고 '만세력 분석하기' 버튼을 클릭하세요."
446
- )
447
-
448
- # 이벤트 연결
449
- calculate_btn.click(
450
- fn=calculate_saju,
451
- inputs=[birth_date, birth_time, gender, birth_place],
452
- outputs=result_output
453
- )
454
-
455
- gr.HTML("""
456
- <div style="text-align: center; padding: 20px; margin-top: 30px; border-top: 1px solid #eee;">
457
- <p><small>※ 본 시스템은 전통 사주명리학을 기반으로 하며, 참고용으로만 활용해주시기 바랍니다.</small></p>
458
- <p><small>※ 정확한 해석을 위해서는 전문가의 상담을 받으시기 바랍니다.</small></p>
459
- </div>
460
- """)
461
 
462
- return demo
463
-
464
- if __name__ == "__main__":
465
- demo = create_interface()
466
- demo.launch(
467
- server_name="0.0.0.0",
468
- server_port=7860,
469
- share=True
470
- )
 
2
  import datetime
3
  import re
4
 
5
+ class AdvancedSajuCalculator:
6
  def __init__(self):
7
  # 천간 (하늘줄기)
8
  self.heavenly_stems = ['갑', '을', '병', '정', '무', '기', '경', '신', '임', '계']
 
19
  '술': '토', '해': '수'
20
  }
21
 
22
+ # 오행별 음양
23
+ self.element_yin_yang = {
24
+ '갑': '양', '을': '음', '병': '양', '정': '음', '무': '양',
25
+ '기': '음', '경': '양', '신': '음', '임': '양', '계': '음',
26
+ '자': '양', '축': '음', '인': '양', '묘': '음', '진': '양',
27
+ '사': '음', '오': '양', '미': '음', '신': '양', '유': '음',
28
+ '술': '양', '해': '음'
29
+ }
30
+
31
+ # 십신 (더 정확한 계산)
32
  self.ten_gods = {
33
+ ('', ''): '비견', ('', ''): '겁재', ('', ''): '식신', ('', '정'): '상관',
34
+ ('', ''): '편재', ('', ''): '정재', ('', ''): '편관', ('', '신'): '정관',
35
+ ('', ''): '편인', ('', ''): '정인',
36
+ ('', ''): '겁재', ('', ''): '비견', ('', ''): '상관', ('', '정'): '식신',
37
+ ('', ''): '정재', ('', ''): '편재', ('', ''): '정관', ('', '신'): '편관',
38
+ ('을', '임'): '정인', ('을', '계'): '편인'
39
  }
40
 
41
+ # 24절기 (양력 기준 근사값)
42
+ self.solar_terms = [
43
+ (2, 4, '입춘'), (2, 19, '우수'), (3, 6, '경칩'), (3, 21, '춘분'),
44
+ (4, 5, '청명'), (4, 20, '곡우'), (5, 6, '입하'), (5, 21, '소만'),
45
+ (6, 6, '망종'), (6, 21, '하지'), (7, 7, '소서'), (7, 23, '대서'),
46
+ (8, 8, '입추'), (8, 23, '처서'), (9, 8, '백로'), (9, 23, '추분'),
47
+ (10, 8, '한로'), (10, 23, '상강'), (11, 7, '입동'), (11, 22, '소설'),
48
+ (12, 7, '대설'), (12, 22, '동지'), (1, 6, '소한'), (1, 20, '대한')
49
+ ]
50
+
51
  # 지역별 시간 보정 (서울 기준 분 단위)
52
  self.location_offsets = {
53
+ '서울': 0, '서울특별시': 0, '경기': 0, '경기도': 0, '인천': 0,
54
+ '강원': +12, '강원도': +12, '춘천': +12, '강릉': +20, '원주': +8,
55
  '충북': -8, '충청북도': -8, '청주': -8, '충주': 0,
56
+ '충남': -16, '충청남도': -16, '대전': -12, '천안': -12,
57
  '전북': -20, '전라북도': -20, '전주': -20, '군산': -24,
58
+ '전남': -24, '전라남도': -24, '광주': -20, '목포': -32, '여수': -16,
59
+ '경북': +8, '경상북도': +8, '대구': +4, '포항': +20, '경주': +16,
60
+ '경남': -4, '경상남도': -4, '부산': +12, '울산': +16, '창원': +4, '진주': -8,
61
  '제주': -20, '제주도': -20, '제주시': -20, '서귀포': -20
62
  }
63
 
64
  def get_location_offset(self, location):
65
+ """출생지별 시간 보정"""
66
  if not location:
67
  return 0
 
68
  location = location.strip()
 
 
 
 
 
 
69
  for key, offset in self.location_offsets.items():
70
  if key in location or location in key:
71
  return offset
72
+ return 0
73
+
74
+ def get_solar_term_month(self, year, month, day):
75
+ """절기를 고려한 정확한 월주 계산"""
76
+ # 입춘 기준으로 년도 결정
77
+ if month == 1 or month == 2:
78
+ # 입춘(2/4) 이전이면 전년도
79
+ lichun_date = datetime.date(year, 2, 4)
80
+ current_date = datetime.date(year, month, day)
81
+ if current_date < lichun_date:
82
+ year -= 1
83
+
84
+ # 절기 기준 월 계산
85
+ month_mapping = {
86
+ (2, 4): 1, # 입춘~경칩 = 정월
87
+ (3, 6): 2, # 경칩~청명 = 2월
88
+ (4, 5): 3, # 청명~입하 = 3월
89
+ (5, 6): 4, # 입하~망종 = 4월
90
+ (6, 6): 5, # 망종~소서 = 5월
91
+ (7, 7): 6, # 소서~입추 = 6월
92
+ (8, 8): 7, # 입추~백로 = 7월
93
+ (9, 8): 8, # 백로~한로 = 8월
94
+ (10, 8): 9, # 한로~입동 = 9월
95
+ (11, 7): 10, # 입동~대설 = 10월
96
+ (12, 7): 11, # 대설~소한 = 11월
97
+ (1, 6): 12 # 소한~입춘 = 12월
98
+ }
99
+
100
+ # 현재 날짜가 어느 절기 구간에 속하는지 확인
101
+ current_date = datetime.date(year if month >= 2 else year + 1, month, day)
102
+
103
+ for (term_month, term_day), lunar_month in month_mapping.items():
104
+ term_date = datetime.date(year if term_month >= 2 else year + 1, term_month, term_day)
105
+ if current_date >= term_date:
106
+ solar_month = lunar_month
107
 
108
+ return year, solar_month
109
 
110
  def parse_date(self, date_str):
111
  """날짜 파싱"""
 
168
  return hour, minute
169
 
170
  def get_ganzhi(self, year, month, day, hour):
171
+ """정확한 간지 계산 (절기 고려)"""
172
+ # 절기 기준 연도와 월 계산
173
+ adjusted_year, solar_month = self.get_solar_term_month(year, month, day)
174
 
175
+ # 기준일: 1900년 1월 1일 (경자년 정축월 갑인일)
176
+ base_date = datetime.date(1900, 1, 1)
177
+ target_date = datetime.date(year, month, day)
178
  days_diff = (target_date - base_date).days
179
 
180
+ # 연주 계산 (절기 기준)
181
+ year_cycle = (adjusted_year - 1900) % 60
182
+ year_stem_index = year_cycle % 10
183
+ year_branch_index = year_cycle % 12
184
 
185
+ # 월주 계산 (절기 기준)
186
+ month_stem_index = (year_stem_index * 2 + solar_month - 1) % 10
187
+ month_branch_index = (solar_month + 1) % 12
188
 
189
  # 일주 계산
190
+ day_cycle = (days_diff + 10) % 60 # 1900.1.1이 갑인일
191
+ day_stem_index = day_cycle % 10
192
+ day_branch_index = day_cycle % 12
193
 
194
  # 시주 계산
195
  hour_branch_index = ((hour + 1) // 2) % 12
 
217
  return element_count
218
 
219
  def get_ten_gods_analysis(self, ganzhi):
220
+ """십신 분석 (정확한 계산)"""
221
  day_stem = ganzhi['day'][0]
 
222
 
223
  analysis = {}
224
  for pillar_name, pillar in ganzhi.items():
225
+ stem = pillar[0]
226
+ branch = pillar[1]
227
+
228
+ # 천간 십신
229
+ if (day_stem, stem) in self.ten_gods:
230
+ stem_relation = self.ten_gods[(day_stem, stem)]
231
+ else:
232
+ # 기본 오행 관계로 계산
233
+ day_element = self.five_elements[day_stem]
234
+ stem_element = self.five_elements[stem]
235
+ stem_relation = self.get_basic_relation(day_element, stem_element)
236
 
237
+ # 지지 십신 (지지의 본기로 계산)
238
+ branch_element = self.five_elements[branch]
239
+ day_element = self.five_elements[day_stem]
240
+ branch_relation = self.get_basic_relation(day_element, branch_element)
241
 
242
  analysis[pillar_name] = {
243
  'stem_relation': stem_relation,
 
246
 
247
  return analysis
248
 
249
+ def get_basic_relation(self, day_element, target_element):
250
+ """기본 오행 관계 계산"""
251
+ relations = {
252
+ '목': {'목': '비견', '화': '식신', '토': '편재', '금': '편관', '수': '편인'},
253
+ '화': {'목': '편인', '화': '비견', '토': '식신', '금': '편재', '수': '편관'},
254
+ '토': {'목': '편관', '화': '편인', '토': '비견', '금': '식신', '수': '편재'},
255
+ '금': {'목': '편재', '화': '편관', '토': '편인', '금': '비견', '수': '식신'},
256
+ '수': {'목': '식신', '화': '편재', '토': '편관', '금': '편인', '수': '비견'}
257
+ }
258
+ return relations[day_element][target_element]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
 
260
  def format_saju_result(calculator, ganzhi, elements, ten_gods, birth_info):
261
+ """사주 결과 포맷팅 - 전문적인 형태"""
262
 
263
+ # 오행별 색상 및 이모지
264
+ element_info = {
265
+ '목': {'color': '#28a745', 'emoji': '🌳', 'name': '목(木)'},
266
+ '화': {'color': '#dc3545', 'emoji': '🔥', 'name': '화(火)'},
267
+ '토': {'color': '#ffc107', 'emoji': '🏔️', 'name': '토(土)'},
268
+ '금': {'color': '#6c757d', 'emoji': '⚔️', 'name': '금(金)'},
269
+ '수': {'color': '#007bff', 'emoji': '💧', 'name': '수(水)'}
270
  }
271
 
272
+ def get_colored_char(char, element):
273
+ info = element_info[element]
274
+ return f'<span style="color: {info["color"]}; font-weight: bold; font-size: 18px;">{char}</span>'
275
 
276
+ def get_element_strength(count):
277
+ if count >= 3:
278
+ return '<span style="color: #dc3545;">**강함**</span>'
279
+ elif count == 2:
280
+ return '<span style="color: #ffc107;">**보통**</span>'
281
+ else:
282
+ return '<span style="color: #6c757d;">**약함**</span>'
283
 
284
  # 시간 보정 정보
285
+ time_info = ""
286
  if birth_info['location_offset'] != 0:
287
  sign = "+" if birth_info['location_offset'] > 0 else ""
288
+ time_info = f"""
289
  ### ⏰ 출생지 시간 보정
290
  - **입력 시간**: {birth_info['original_time']}
291
+ - **보정 시간**: {birth_info['corrected_time']}
292
+ - **보정값**: {birth_info['birth_place']} 기준 {sign}{birth_info['location_offset']}분
293
  """
294
 
295
+ # 사주 표 생성
296
+ year_stem = get_colored_char(ganzhi['year'][0], calculator.five_elements[ganzhi['year'][0]])
297
+ year_branch = get_colored_char(ganzhi['year'][1], calculator.five_elements[ganzhi['year'][1]])
298
+ month_stem = get_colored_char(ganzhi['month'][0], calculator.five_elements[ganzhi['month'][0]])
299
+ month_branch = get_colored_char(ganzhi['month'][1], calculator.five_elements[ganzhi['month'][1]])
300
+ day_stem = get_colored_char(ganzhi['day'][0], calculator.five_elements[ganzhi['day'][0]])
301
+ day_branch = get_colored_char(ganzhi['day'][1], calculator.five_elements[ganzhi['day'][1]])
302
+ hour_stem = get_colored_char(ganzhi['hour'][0], calculator.five_elements[ganzhi['hour'][0]])
303
+ hour_branch = get_colored_char(ganzhi['hour'][1], calculator.five_elements[ganzhi['hour'][1]])
304
+
305
  result = f"""
306
+ # 🔮 사주명리 만세력 완전분석
307
 
308
+ ## 📋 출생 정보
309
+ - **생년월일**: {birth_info['birth_datetime'].strftime('%Y년 %m월 %d일')} (양력)
310
+ - **출생시간**: {birth_info['corrected_time']}
311
  - **성별**: {birth_info['gender']}
312
+ - **출생지**: {birth_info['birth_place']}
313
+ {time_info}
314
 
315
+ ## 🏛️ 사주(四柱) 만세력 【절기 기준 정확 계산】
316
 
317
+ <div style="background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); padding: 20px; border-radius: 10px; margin: 20px 0;">
318
+
319
+ | **구분** | **시주(時柱)** | **일주(日柱)** | **월주(月柱)** | **연주(年柱)** |
320
+ |:--------:|:-------------:|:-------------:|:-------------:|:-------------:|
321
+ | **천간** | {hour_stem} | {day_stem} | {month_stem} | {year_stem} |
322
+ | **지지** | {hour_branch} | {day_branch} | {month_branch} | {year_branch} |
323
+ | **간지** | **{ganzhi['hour'][0]}{ganzhi['hour'][1]}** | **{ganzhi['day'][0]}{ganzhi['day'][1]}** | **{ganzhi['month'][0]}{ganzhi['month'][1]}** | **{ganzhi['year'][0]}{ganzhi['year'][1]}** |
324
+ | **의미** | 자식·말년 | 본인·배우자 | 부모·청년 | 조상·유년 |
325
+ | **오행** | {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']} |
326
 
327
  </div>
328
 
329
+ > **일간**: {get_colored_char(ganzhi['day'][0], calculator.five_elements[ganzhi['day'][0]])} - 당신의 본성을 나타내는 핵심 요소
330
 
331
+ ## 🌟 오행(五行) 균형 분석
332
 
333
+ <div style="background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 15px 0;">
 
 
 
 
 
 
334
 
335
+ | **오행** | **개수** | **비율** | **강약** | **특성** |
336
+ |:--------:|:--------:|:--------:|:--------:|:--------:|
337
+ | {element_info['목']['emoji']} **목(木)** | **{elements['목']}개** | {(elements['목']/8*100):.1f}% | {get_element_strength(elements['목'])} | 성장·창조·유연성 |
338
+ | {element_info['화']['emoji']} **화(火)** | **{elements['화']}개** | {(elements['화']/8*100):.1f}% | {get_element_strength(elements['화'])} | 열정·활동·표현력 |
339
+ | {element_info['토']['emoji']} **토(土)** | **{elements['토']}개** | {(elements['토']/8*100):.1f}% | {get_element_strength(elements['토'])} | 안정·신뢰·포용력 |
340
+ | {element_info['금']['emoji']} **금(金)** | **{elements['금']}개** | {(elements['금']/8*100):.1f}% | {get_element_strength(elements['금'])} | 정의·결단·리더십 |
341
+ | {element_info['수']['emoji']} **수(水)** | **{elements['수']}개** | {(elements['수']/8*100):.1f}% | {get_element_strength(elements['수'])} | 지혜·적응·사고력 |
342
 
343
+ </div>
 
 
 
 
 
 
 
344
 
345
+ ## 🎭 십신(十神) 관계 분석
 
346
 
347
+ <div style="background: #fff3cd; padding: 15px; border-radius: 8px; margin: 15px 0;">
348
 
349
+ | **주(柱)** | **천간 십신** | **지지 십신** | **의미** |
350
+ |:----------:|:------------:|:------------:|:--------:|
351
+ | **시주** | {ten_gods['hour']['stem_relation']} | {ten_gods['hour']['branch_relation']} | 자식운·말년운 |
352
+ | **일주** | {ten_gods['day']['stem_relation']} | {ten_gods['day']['branch_relation']} | 본인·결혼운 |
353
+ | **월주** | {ten_gods['month']['stem_relation']} | {ten_gods['month']['branch_relation']} | 부모운·사회운 |
354
+ | **연주** | {ten_gods['year']['stem_relation']} | {ten_gods['year']['branch_relation']} | 조상운·초년운 |
355
 
356
  </div>
357
 
358
+ ## 💡 종합 운세 해석
359
 
360
+ ### 🎯 일간 분석 (핵심 성격)
361
+ 일간 **{get_colored_char(ganzhi['day'][0], calculator.five_elements[ganzhi['day'][0]])}**({element_info[calculator.five_elements[ganzhi['day'][0]]]['name']})의 특성:
 
362
 
363
+ {get_detailed_personality(calculator.five_elements[ganzhi['day'][0]])}
364
+
365
+ ### ⚖️ 오행 균형 진단
366
+ {get_advanced_balance_advice(elements)}
367
+
368
+ ### 🔮 운세 포인트
369
+ {get_fortune_points(ganzhi, elements, calculator)}
370
 
371
  ---
372
 
373
+ ### 📌 분석 기준
374
+ - **절기 기준 계산**: 입춘을 기준으로 정확한 연주·월주 산정
375
+ - **출생지 시간 보정**: 지역별 경도차 반영
376
+ - **전통 명리학**: 정통 사주명리학 이론 적용
377
+
378
+ ### ⚠️ 유의사항
379
+ 본 분석은 전통 사주명리학을 기반으로 한 기본 해석입니다. 더 정확하고 개인화된 분석을 위해서는 전문가 상담을 권장드립니다.
380
 
381
+ ---
382
+ *분석 완료: {datetime.datetime.now().strftime('%Y년 %m월 %d일 %H시 %M분')}*
383
  """
384
 
385
  return result
386
 
387
+ def get_detailed_personality(element):
388
+ """상세한 성격 분석"""
389
+ personalities = {
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
+ return personalities.get(element, "균형 잡힌 성격을 가지고 있습니다.")
421
+
422
+ def get_advanced_balance_advice(elements):
423
+ """고급 오행 균형 조언"""
424
+ max_element = max(elements, key=elements.get)
425
+ min_element = min(elements, key=elements.get)
426
+ max_count = elements[max_element]
427
+ min_count = elements[min_element]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
428
 
429
+ advice = f"""
430
+ **🔸 강한 오행**: {max_element} ({max_count}개) - 이 기운이 강하게 나타남
431
+ **🔸 약한 오행**: {min_element} ({min_count}개) - 이 기운을 보강하면 좋음
432
+
433
+ **💫 균형 조절 방법**:
434
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
435
 
436
+ if max_count - min_count > 2:
437
+ advice += f"- {min_element}를 보강하는 색상, 방향, 직업을 선택\n"
438
+ advice += f"- {max_element}의 과도한 기운을 조절하는 것이 필요\n"
439
+ advice += "- 전체적인 조화를 위한 노력이 중요"
440
+ else:
441
+ advice += "- 현재 오행이 비교적 균형잡혀 있는 상태\n"
442
+ advice += "- 기존의 균형을 유지하면서 발전시키는 것이