auto: 일일 백업 2026-08-13 02:00

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
hyowons
2026-08-13 02:00:00 +09:00
parent c93d1ac385
commit 6428846506
127 changed files with 1822 additions and 1688 deletions
@@ -1,12 +1,15 @@
#!/usr/bin/env python3
"""stock.briefing 폴백·강제 재실행.
메일은 발송하지 않는다 (2026-08-12 관리자님 결정 — 주식 리포트 메일 중지, 스냅샷만 유지).
목적은 자산웹 '당일 평가손익' 기준값이 되는 오늘 스냅샷을 확보하는 것.
retry/final: 오늘 portfolio_daily_snapshot.json에 오늘 키가 이미 있으면 skip (idempotent).
없으면 stock_portfolio_report.py send 재실행. final 모드에선 그래도 실패 시 레이 텔레그램 알림.
없으면 stock_portfolio_report.py send --no-mail 재실행. final 모드에선 그래도 실패 시 레이 텔레그램 알림.
force: 스냅샷 존재 여부 무관하게 21:00 fresh fetch로 데이터 덮어쓰기. 20:10 데이터가
부정확할 때 21:00 안정된 데이터로 갱신용. 스냅샷 있으면 run 모드(스냅샷만, 메일·텔레그램 X),
없으면 send 모드로 폴백 + 실패 시 알림.
부정확할 때 21:00 안정된 데이터로 갱신용. 스냅샷 있으면 run 모드, 없으면 send --no-mail 폴백
+ 실패 시 알림.
usage:
briefing_fallback.py retry # 20:30 — 스냅샷 없으면 재실행, 실패해도 알림 없음
@@ -42,7 +45,9 @@ def today_snapshot_exists() -> bool:
def run_briefing() -> int:
return subprocess.call(['/usr/bin/python3', str(REPORT), 'send'])
# 2026-08-12부터 주식 리포트 메일은 발송하지 않는다 (스냅샷만). --no-mail 은 send 경로의
# 휴장일 가드·실패 알림을 유지하면서 메일만 건너뛴다.
return subprocess.call(['/usr/bin/python3', str(REPORT), 'send', '--no-mail'])
def alert(text: str) -> None:
@@ -69,13 +74,13 @@ def main() -> int:
if mode == 'force':
# 21:00 무조건 재실행 — 20:10 데이터가 부정확할 때 fresh fetch로 덮어씀.
# 스냅샷 있으면 run(스냅샷만), 없으면 send(메일·텔레그램·스냅샷 + 실패 시 알림).
# 스냅샷 있으면 run(스냅샷만), 없으면 send --no-mail(스냅샷 + 실패 시 알림).
if today_snapshot_exists():
print(f'[force] {now} 스냅샷 갱신 (run 모드, 메일·텔레그램 없음).')
code = subprocess.call(['/usr/bin/python3', str(REPORT), 'run'])
print(f'[force] {now} run exit={code}')
return 0 if code == 0 else 1
print(f'[force] {now} 스냅샷 없음 → send 재실행 (메일·텔레그램 포함).')
print(f'[force] {now} 스냅샷 없음 → send --no-mail 재실행 (메일 없음).')
code = run_briefing()
print(f'[force] {now} send exit={code}')
if today_snapshot_exists():
@@ -83,7 +88,7 @@ def main() -> int:
alert(
f'⚠️ stock.briefing {now} 폴백 모두 실패\n'
f'오늘({datetime.now(KST):%Y-%m-%d}) 스냅샷이 생성되지 않았습니다.\n'
f'수동 실행: python3 {REPORT} send'
f'수동 실행: python3 {REPORT} send --no-mail'
)
return 1
@@ -103,7 +108,7 @@ def main() -> int:
alert(
f'⚠️ stock.briefing {now} 폴백 모두 실패\n'
f'오늘({datetime.now(KST):%Y-%m-%d}) 스냅샷이 생성되지 않았습니다.\n'
f'수동 실행: python3 {REPORT} send'
f'수동 실행: python3 {REPORT} send --no-mail'
)
return 1 if mode == 'final' else 0
@@ -1280,9 +1280,12 @@ def main() -> int:
args = sys.argv[1:]
mode = 'run'
by_account = False
no_mail = False
for a in args:
if a == '--by-account':
by_account = True
elif a == '--no-mail':
no_mail = True
elif a in ('run', 'send'):
mode = a
else:
@@ -1291,7 +1294,7 @@ def main() -> int:
print(
f"[START] stock_portfolio_report mode={mode} by_account={by_account} "
f"ts={started_at.isoformat()} pid={os.getpid()}"
f"no_mail={no_mail} ts={started_at.isoformat()} pid={os.getpid()}"
)
# send 모드는 휴장일/주말이면 즉시 스킵 — 시세·잔고 무변동이라 메일이 어제와 동일.
@@ -1318,6 +1321,12 @@ def main() -> int:
subject = f"[주식 리포트] {datetime.now(KST):%Y-%m-%d} 20:00"
if mode == 'send':
# --no-mail: 스냅샷만 남기는 스케줄 실행용. 휴장일 self-skip·실패 알림은 그대로 유지해야
# 하므로 run 모드가 아니라 send 경로에서 메일 호출만 건너뛴다.
if no_mail:
print('MAIL_SKIPPED=no_mail_flag')
print(f"[END] stock_portfolio_report status=ok ts={datetime.now(KST).isoformat()}")
return 0
mail_ok = False
try:
send_result = send_mail(subject, html_body, html=True)