goheung/app/api/weather.py
2026-02-02 19:07:53 +09:00

127 lines
3.7 KiB
Python

from flask import Blueprint, jsonify
import json
import jsonify
import os
import csv
import time
import xml.etree.ElementTree as ET
from datetime import datetime
import requests
import app.data_config as data_config
"""
-------------------------------------------------------------------------
File: weather.py
Description: 고흥 날씨를 가져오는 기상청 API
Author: 소지안 프로
Created: 2026-02-02
Last Modified: 2026-02-02
-------------------------------------------------------------------------
"""
bp = Blueprint('kma', __name__, url_prefix='/api/kma')
# 캐시 저장 경로 (app 루트 기준 data/kma_cache.json)
CACHE_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'data')
CACHE_FILE = os.path.join(CACHE_DIR, 'kma_cache.json')
def _ensure_cache_dir():
"""캐시 디렉토리 생성"""
os.makedirs(CACHE_DIR, exist_ok=True)
def _fetch_weather_prediction_kma_api():
"""
기상청 API에서 육상예보 데이터 조회
"""
url = 'https://apihub.kma.go.kr/api/typ02/openApi/VilageFcstMsgService/getLandFcst'
params = {
'authKey': data_config.KMA_AUTH_KEY,
'dataType': 'JSON',
'regId': '11F20403',
'pageNo': '1',
'numOfRows': '30',
}
response = requests.get(url, params=params, timeout=15)
response.raise_for_status()
data = response.json()
body = data.get('response', {}).get('body', {})
items = body.get('items', {}).get('item', [])
if not isinstance(items, list):
items = [items] if items else []
rows = []
for item in items:
rows.append({
'announceTime': item.get('announceTime', ''),
'numEf': item.get('numEf', ''),
'wf': item.get('wf', ''),
'rnSt': item.get('rnSt', ''),
'rnYn': item.get('rnYn', ''),
'ta': item.get('ta', ''),
'wfCd': item.get('wfCd', ''),
'wd1': item.get('wd1', ''),
'wd2': item.get('wd2', ''),
'wdTnd': item.get('wdTnd', ''),
'wsIt': item.get('wsIt', ''),
})
return {'success': True, 'rows': rows, 'count': len(rows)}
def load_cached_forecast():
"""저장된 캐시에서 예보 데이터 로드"""
if not os.path.exists(CACHE_FILE):
return None
try:
with open(CACHE_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
except (json.JSONDecodeError, IOError):
return None
def save_forecast_cache(data):
"""예보 데이터를 캐시 파일로 저장"""
_ensure_cache_dir()
payload = {
'cachedAt': datetime.now().isoformat(),
'data': data,
}
with open(CACHE_FILE, 'w', encoding='utf-8') as f:
json.dump(payload, f, ensure_ascii=False, indent=2)
def fetch_and_cache_forecast():
"""
기상청 API를 호출하여 예보를 가져와 서버에 캐시 저장.
매일 밤 12시 스케줄에서 호출됨.
TODO : 스케줄링 일시 변경 가능
"""
try:
data = _fetch_weather_prediction_kma_api()
if data.get('success'):
save_forecast_cache(data)
return True
except Exception:
pass
return False
@bp.route('/forecast', methods=['GET'])
def get_forecast():
"""
기상청 육상예보 조회 - 캐시가 있으면 캐시 반환, 없으면 API 호출
"""
cached = load_cached_forecast()
if cached and cached.get('data'):
return jsonify(cached['data'])
try:
data = _fetch_weather_prediction_kma_api()
if data.get('success'):
save_forecast_cache(data)
return jsonify(data)
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 500