127 lines
3.7 KiB
Python
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
|