feat: initial commit - unified FGTools from static, weather, mattermost-noti

This commit is contained in:
2025-12-31 09:56:37 +09:00
commit 4ff5dba4b1
29 changed files with 5786 additions and 0 deletions

View File

@ -0,0 +1,8 @@
# ===================================================================
# apps/weather_api/__init__.py
# 날씨 API 앱 패키지 초기화
# ===================================================================
from .app import create_app, weather_bp, run_server
__all__ = ['create_app', 'weather_bp', 'run_server']

243
apps/weather_api/app.py Normal file
View File

@ -0,0 +1,243 @@
# ===================================================================
# apps/weather_api/app.py
# 날씨 API 서버 애플리케이션
# ===================================================================
# 기상청 API 데이터를 제공하는 REST API 서버입니다.
# 강수량 예보, 날씨 캡처 등의 기능을 제공합니다.
# ===================================================================
"""
날씨 API 서버 애플리케이션
기상청 API 데이터를 조회하고 가공하여 제공하는 API 서버입니다.
사용 예시:
from apps.weather_api.app import create_app
app = create_app()
app.run()
"""
from flask import Flask, Blueprint, jsonify, request
from werkzeug.exceptions import HTTPException
from core.config import get_config
from core.logging_utils import get_logger, setup_logging
logger = get_logger(__name__)
# Blueprint 생성
weather_bp = Blueprint('weather', __name__, url_prefix='/api/weather')
@weather_bp.route('/health', methods=['GET'])
def health_check():
"""헬스 체크"""
return jsonify({
'status': 'healthy',
'service': 'weather-api'
})
@weather_bp.route('/precipitation', methods=['GET'])
def get_precipitation():
"""
시간별 강수량 예보 조회
Query Parameters:
date: 조회 날짜 (YYYYMMDD, 기본: 오늘)
format: 출력 형식 (json, html, text)
Returns:
강수량 예보 데이터
"""
from services.weather import PrecipitationService
target_date = request.args.get('date')
output_format = request.args.get('format', 'json')
try:
service = PrecipitationService()
data = service.get_precipitation_info(
target_date=target_date,
output_format=output_format
)
if output_format == 'html':
return data, 200, {'Content-Type': 'text/html; charset=utf-8'}
elif output_format == 'text':
return data, 200, {'Content-Type': 'text/plain; charset=utf-8'}
else:
return jsonify({
'status': 'success',
'data': data
})
except Exception as e:
logger.error(f"강수량 조회 실패: {e}")
return jsonify({'error': str(e)}), 500
@weather_bp.route('/forecast/ultra', methods=['GET'])
def get_ultra_forecast():
"""
초단기예보 조회
Returns:
초단기예보 데이터
"""
from services.weather import get_daily_ultra_forecast
config = get_config()
service_key = config.data_api.get('service_key') or config.weather_service.get('service_key', '')
if not service_key:
return jsonify({'error': 'API 키가 설정되지 않았습니다.'}), 500
try:
data = get_daily_ultra_forecast(service_key)
return jsonify({
'status': 'success',
'type': 'ultra',
'data': data
})
except Exception as e:
logger.error(f"초단기예보 조회 실패: {e}")
return jsonify({'error': str(e)}), 500
@weather_bp.route('/forecast/vilage', methods=['GET'])
def get_vilage_forecast():
"""
단기예보 조회
Returns:
단기예보 데이터
"""
from services.weather import get_daily_vilage_forecast
config = get_config()
service_key = config.data_api.get('service_key') or config.weather_service.get('service_key', '')
if not service_key:
return jsonify({'error': 'API 키가 설정되지 않았습니다.'}), 500
try:
data = get_daily_vilage_forecast(service_key)
return jsonify({
'status': 'success',
'type': 'vilage',
'data': data
})
except Exception as e:
logger.error(f"단기예보 조회 실패: {e}")
return jsonify({'error': str(e)}), 500
@weather_bp.route('/forecast/midterm', methods=['GET'])
def get_midterm_forecast():
"""
중기예보 조회
Query Parameters:
reg_id: 지역 코드 (기본: 11B20305)
Returns:
중기예보 데이터
"""
from services.weather import get_midterm_forecast as fetch_midterm
config = get_config()
service_key = config.data_api.get('service_key') or config.weather_service.get('service_key', '')
reg_id = request.args.get('reg_id', '11B20305')
if not service_key:
return jsonify({'error': 'API 키가 설정되지 않았습니다.'}), 500
try:
precip_probs, raw_data = fetch_midterm(service_key, reg_id)
return jsonify({
'status': 'success',
'type': 'midterm',
'precipitation_probability': precip_probs,
'raw_data': raw_data
})
except Exception as e:
logger.error(f"중기예보 조회 실패: {e}")
return jsonify({'error': str(e)}), 500
@weather_bp.errorhandler(HTTPException)
def handle_exception(e):
"""HTTP 예외 핸들러"""
return jsonify({
'error': e.description,
'status_code': e.code
}), e.code
def create_app(config_override: dict = None) -> Flask:
"""
Flask 애플리케이션 팩토리
Args:
config_override: 설정 덮어쓰기 딕셔너리
Returns:
Flask 앱 인스턴스
"""
app = Flask(__name__)
# 설정 로드
config = get_config()
app.config['SECRET_KEY'] = config.flask.get('secret_key', 'dev-secret')
app.config['JSON_AS_ASCII'] = False
if config_override:
app.config.update(config_override)
# 로깅 설정
setup_logging('apps.weather_api', level=config.log_level)
# Blueprint 등록
app.register_blueprint(weather_bp)
# 루트 엔드포인트
@app.route('/')
def index():
return jsonify({
'name': 'FGTools Weather API',
'version': '1.0.0',
'endpoints': [
'/api/weather/health',
'/api/weather/precipitation',
'/api/weather/forecast/ultra',
'/api/weather/forecast/vilage',
'/api/weather/forecast/midterm',
]
})
logger.info("Weather API 앱 초기화 완료")
return app
def run_server(host: str = '0.0.0.0', port: int = 5001, debug: bool = False):
"""
개발 서버 실행
Args:
host: 바인딩 호스트
port: 포트 번호
debug: 디버그 모드
"""
app = create_app()
app.run(host=host, port=port, debug=debug)
if __name__ == '__main__':
config = get_config()
run_server(
host=config.flask.get('host', '0.0.0.0'),
port=5001,
debug=config.debug
)