From d54a85c14a2f2320c9c4b21f18438322c6e737fd Mon Sep 17 00:00:00 2001 From: WoongyuChoi Date: Thu, 5 Dec 2024 14:34:11 +0900 Subject: [PATCH 1/2] =?UTF-8?q?featrue=20:=20Flask=20=EC=9D=B8=EC=BD=94?= =?UTF-8?q?=EB=94=A9=20ASCII=20->=20UTF-8=20=EB=B3=80=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/server.py | 6 ++++-- config.py | 1 + decorators/__init__.py | 6 +++++- decorators/json_response.py | 16 ++++++++++++++++ 4 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 decorators/json_response.py diff --git a/api/server.py b/api/server.py index bf85830..1ea3668 100644 --- a/api/server.py +++ b/api/server.py @@ -1,10 +1,11 @@ import time -from flask import Flask, jsonify, request +from flask import Flask, jsonify from flask_caching import Cache from api import ExternalAPI from config import Config +from decorators import json_utf8_response from handler.exception_handler import register_exception_handlers from handler.logger import get_logger from utils import get_request_params @@ -36,6 +37,7 @@ def favicon(ext): @app.route("/api/exchange-rate", methods=["GET"]) +@json_utf8_response @cache.cached(query_string=True) def get_exchange_rate(): params = get_request_params("start_date", "end_date", "item_code") @@ -46,7 +48,7 @@ def get_exchange_rate(): item_code=params["item_code"], ) logger.info("Exchange rate data fetched successfully.") - return jsonify(data), 200 + return data, 200 def handler(event, context): diff --git a/config.py b/config.py index 43ca550..77533a0 100644 --- a/config.py +++ b/config.py @@ -2,3 +2,4 @@ class Config: CACHE_TYPE = "FileSystemCache" CACHE_DIR = "/tmp" CACHE_DEFAULT_TIMEOUT = 60 + JSON_AS_ASCII = False diff --git a/decorators/__init__.py b/decorators/__init__.py index ac1aa81..6ab9803 100644 --- a/decorators/__init__.py +++ b/decorators/__init__.py @@ -1,3 +1,7 @@ from .param_defaults import default_params +from .json_response import json_utf8_response -__all__ = ["default_params"] +__all__ = [ + "default_params", + "json_utf8_response", +] diff --git a/decorators/json_response.py b/decorators/json_response.py new file mode 100644 index 0000000..f8ba89e --- /dev/null +++ b/decorators/json_response.py @@ -0,0 +1,16 @@ +from functools import wraps +from flask import Response +import json + + +def json_utf8_response(func): + @wraps(func) + def wrapper(*args, **kwargs): + result, status_code = func(*args, **kwargs) + return Response( + json.dumps(result, ensure_ascii=False), + mimetype="application/json", + status=status_code, + ) + + return wrapper From de24247da8008ffa70b3ec888617fb0a06888749 Mon Sep 17 00:00:00 2001 From: WoongyuChoi Date: Thu, 5 Dec 2024 15:30:52 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feature=20:=20=EC=99=B8=ED=99=98=EB=B3=B4?= =?UTF-8?q?=EC=9C=A0=EC=95=A1(=ED=95=A9=EA=B3=84)=20API=20=EC=8B=A0?= =?UTF-8?q?=EA=B7=9C=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/data_processor.py | 6 +++--- api/external.py | 23 ++++++++++++++++++++++- api/server.py | 15 +++++++++++++-- decorators/param_defaults.py | 29 ++++++++++++++++++++++++++--- utils/__init__.py | 9 ++++++++- utils/date_utils.py | 24 ++++++++++++++++++++---- 6 files changed, 92 insertions(+), 14 deletions(-) diff --git a/api/data_processor.py b/api/data_processor.py index 7d40f03..3021686 100644 --- a/api/data_processor.py +++ b/api/data_processor.py @@ -9,7 +9,7 @@ class DataProcessor: """ @staticmethod - def process_exchange_rate_data(response_data): + def process_statistic_data(response_data): if "StatisticSearch" not in response_data: logger.error(f"ValueError: {str(response_data)}") @@ -27,8 +27,8 @@ def process_exchange_rate_data(response_data): processed_data.append( { "value": value, - "item_code": row.get("ITEM_CODE1"), - "item_name": row.get("ITEM_NAME1"), + "itemCode": row.get("ITEM_CODE1"), + "itemName": row.get("ITEM_NAME1"), "time": row.get("TIME"), "unit": ( row.get("UNIT_NAME").strip() if row.get("UNIT_NAME") else None diff --git a/api/external.py b/api/external.py index 5045f5d..6ee4098 100644 --- a/api/external.py +++ b/api/external.py @@ -48,4 +48,25 @@ def fetch_exchange_rate(start_date, end_date, item_code=None): url = generate_statistic_url(params) response_data = fetch_data(url) - return DataProcessor.process_exchange_rate_data(response_data) + return DataProcessor.process_statistic_data(response_data) + + @staticmethod + @default_params + def fetch_foreign_reserves(start_month, end_month): + """ + 외부 API를 호출하여 외환보유액 데이터를 조회합니다. + """ + + params = APIParams( + service_name="StatisticSearch", + table_code="732Y001", + period="M", + start_date=start_month, + end_date=end_month, + item_code="99", + ) + + url = generate_statistic_url(params) + response_data = fetch_data(url) + + return DataProcessor.process_statistic_data(response_data) diff --git a/api/server.py b/api/server.py index 1ea3668..9215894 100644 --- a/api/server.py +++ b/api/server.py @@ -21,7 +21,6 @@ @app.route("/") def health_check(): - logger.info("Health check called.") status = { "status": "UP", "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"), @@ -47,7 +46,19 @@ def get_exchange_rate(): end_date=params["end_date"], item_code=params["item_code"], ) - logger.info("Exchange rate data fetched successfully.") + return data, 200 + + +@app.route("/api/foreign-reserves", methods=["GET"]) +@cache.cached(query_string=True) +@json_utf8_response +def get_foreign_reserves(): + params = get_request_params("start_month", "end_month") + + data = ExternalAPI.fetch_foreign_reserves( + start_month=params["start_month"], + end_month=params["end_month"], + ) return data, 200 diff --git a/decorators/param_defaults.py b/decorators/param_defaults.py index 4a207a2..1fa5087 100644 --- a/decorators/param_defaults.py +++ b/decorators/param_defaults.py @@ -1,12 +1,35 @@ +import inspect from functools import wraps -from utils import get_first_day_of_last_month, get_last_day_of_last_month + +from utils import ( + get_first_day_of_last_month, + get_first_month_of_last_year, + get_last_day_of_last_month, + get_last_month_of_last_year, +) def default_params(func): @wraps(func) def wrapper(*args, **kwargs): - kwargs["start_date"] = kwargs.get("start_date") or get_first_day_of_last_month() - kwargs["end_date"] = kwargs.get("end_date") or get_last_day_of_last_month() + func_signature = inspect.signature(func) + func_params = func_signature.parameters + + if "start_date" in func_params: + kwargs["start_date"] = ( + kwargs.get("start_date") or get_first_day_of_last_month() + ) + if "end_date" in func_params: + kwargs["end_date"] = kwargs.get("end_date") or get_last_day_of_last_month() + if "start_month" in func_params: + kwargs["start_month"] = ( + kwargs.get("start_month") or get_first_month_of_last_year() + ) + if "end_month" in func_params: + kwargs["end_month"] = ( + kwargs.get("end_month") or get_last_month_of_last_year() + ) + return func(*args, **kwargs) return wrapper diff --git a/utils/__init__.py b/utils/__init__.py index aa00bc6..c590ae9 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -1,10 +1,17 @@ -from .date_utils import get_first_day_of_last_month, get_last_day_of_last_month +from .date_utils import ( + get_first_day_of_last_month, + get_last_day_of_last_month, + get_first_month_of_last_year, + get_last_month_of_last_year, +) from .fetch_utils import fetch_data, generate_statistic_url from .request_utils import get_request_params __all__ = [ "get_first_day_of_last_month", "get_last_day_of_last_month", + "get_first_month_of_last_year", + "get_last_month_of_last_year", "fetch_data", "generate_statistic_url", "get_request_params", diff --git a/utils/date_utils.py b/utils/date_utils.py index 5750272..1fc44f3 100644 --- a/utils/date_utils.py +++ b/utils/date_utils.py @@ -4,8 +4,7 @@ def get_first_day_of_last_month(): """ - 지난달의 첫날을 반환합니다. - :return: 지난달의 첫날 (YYYYMMDD 형식) + 지난달의 첫날(yyyyMMdd)을 반환합니다. """ today = datetime.today() last_day_of_last_month = today.replace(day=1) - timedelta(days=1) @@ -15,9 +14,26 @@ def get_first_day_of_last_month(): def get_last_day_of_last_month(): """ - 지난달의 마지막 날을 반환합니다. - :return: 지난달의 마지막 날 (YYYYMMDD 형식) + 지난달의 마지막 날(yyyyMMdd)을 반환합니다. """ today = datetime.today() last_day_of_last_month = today.replace(day=1) - timedelta(days=1) return last_day_of_last_month.strftime("%Y%m%d") + + +def get_first_month_of_last_year(): + """ + 지난 해의 첫 달(yyyyMM)을 반환합니다. + """ + today = datetime.today() + first_month = today.replace(year=today.year - 1, month=1, day=1) + return first_month.strftime("%Y%m") + + +def get_last_month_of_last_year(): + """ + 지난 해의 마지막 달(yyyyMM)을 반환합니다. + """ + today = datetime.today() + last_month = today.replace(year=today.year - 1, month=12, day=1) + return last_month.strftime("%Y%m")