auto: 일일 백업 2026-08-13 02:00
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user