fastapi 엑셀 파일 불러와서 뿌려주기 #3 - Refactoring
"""
main.py
@제목: 메인 실행 파일
@설명: 메인 실행 파일
작성일자 작성자
-----------------------
2024.03.14 hiio420
"""
import math
import pandas as pd
from fastapi import FastAPI,Depends
from db.initDB import SessionLocal,get_user
from sqlalchemy.orm import sessionmaker,declarative_base,relationship,Session
# 엑셀 파일 불러오기
xlsx = pd.ExcelFile("F:\\[01]project\\commonStandardTerm\\upload\\(붙임)공공데이터 공통표준용어(2022.7월).xlsx")
# 각 시트 별 DataFrame 생성
df_cmStdTrm = pd.read_excel("F:\\[01]project\\commonStandardTerm\\upload\\(붙임)공공데이터 공통표준용어(2022.7월).xlsx",
sheet_name=xlsx.sheet_names[0]).fillna("")
df_cmStdWd = pd.read_excel("F:\\[01]project\\commonStandardTerm\\upload\\(붙임)공공데이터 공통표준용어(2022.7월).xlsx",
sheet_name=xlsx.sheet_names[1]).fillna("")
df_cmStdDmn = pd.read_excel("F:\\[01]project\\commonStandardTerm\\upload\\(붙임)공공데이터 공통표준용어(2022.7월).xlsx",
sheet_name=xlsx.sheet_names[2]).fillna("")
df_cmStdTmplt = pd.read_excel("F:\\[01]project\\commonStandardTerm\\upload\\(붙임)공공데이터 공통표준용어(2022.7월).xlsx",
sheet_name=xlsx.sheet_names[3]).fillna("")
# DataFrame을 Value로 하고 키가 int인 딕셔너리 생성
df_dict = {
0: df_cmStdTrm,
1: df_cmStdWd,
2: df_cmStdDmn,
3: df_cmStdTmplt
}
app = FastAPI()
@app.get("/")
def read_root(data_id: int = 0, page: int = 1, limit: int = 10,db: Session = Depends(get_db)):
if page < 1:
page = 0
if limit < 10:
limit = 10
df = df_dict[data_id]
first_idx = (page - 1) * limit
last_idx = first_idx + limit - 1
total_size = df.shape[0]
first_page = 1
last_page = math.ceil(total_size / limit)
df_sliced = df[first_idx:last_idx + 1].copy()
df_sliced["번호"] = [i for i in range(total_size - (page - 1) * limit, total_size - (page - 1) * limit - df_sliced.shape[0], -1)]
data = []
if df_sliced.shape[0] != 0:
data = df_sliced.to_dict("records")
return {"sheetName": xlsx.sheet_names[data_id], "data_id": data_id, "page": page, "limit": limit,
"first_page": first_page, "last_page": last_page, "firstIdx": first_idx, "lastIdx": last_idx,
"totalSize": total_size,
"data": data}
if __name__ == "__main__":
print(xlsx.sheet_names)
print([(key, value.size) for key, value in df_dict.items()])
현재 까지 코드는 위와 같이 되어 있다 . 파일을 불러와서 쿼리스트링에 맞춰서 제공하는데 기능은 잘 작동하지만 코드가 너무 지저분한 느낌
파일을 읽어와서 딕셔너리로 변환하는 부분까지 우선 class 로 만들어서 사용하면 좋겠다는 생각을 했다.
1. 디렉토리 만들기
서버에서 사용하는 유틸성 있는 class나 함수들을 libs 디렉토리 안에 utils 디렉토리를 만들어서 관리하면 어떨까 한다.
pycharm에서 Python Package 만들기로 만들어 주자
2. FileUtils Class 만들기
먼저 excel 파일을 만들기 전에 파일을 읽어와서 파일에 대한 데이터를 가지고 있는 공통 class를 만들면 어떨까 싶어서
FileUtils Class를 만들어 보려고 한다.
1. 파일을 읽고
2. 파일 정보를 멤버 변수에 할당합니다.
3. class를 dict 와 json으로 변환 할 수 있도록 합니다.
4. 파일 사이즈를 Byte,KB,MB,GB 등로 변환합니다.
파일 정보는
1. 파일이름
2. 파일 경로
3. 파일 확장자
4. 파일 사이즈
5. 파일 사이즈 + 용량단위
6. 파일 마지막 사용 날짜
7. 파일 생성 날짜
8. 파일 마지막 수정 날짜
입니다.
"""
FileUtils.py
@제목: 파일 정보 파일
@설명: 파일 정보를 읽어오는 Class 파일
작성일자 작성자
-----------------------
2024.03.15 hiio420
"""
import os
import pathlib
from datetime import datetime
import json
from typing import Any
class FileUtils:
def __init__(self):
self.filename: str | None = None
self.path: str | None = None
self.ext: str | None = None
self.size: int | None = None
self.size_unit: str | None = None
self.access_dt: datetime | None = None
self.created_dt: datetime | None = None
self.modified_dt: datetime | None = None
def read_file(self, path):
self.path = path
self.size = os.path.getsize(path)
self.size_unit = self.calc_size_unit(self.size)
self.access_dt = datetime.fromtimestamp(os.path.getatime(path))
self.created_dt = datetime.fromtimestamp(os.path.getctime(path))
self.modified_dt = datetime.fromtimestamp(os.path.getmtime(path))
self.ext = pathlib.Path(path).suffix
self.filename = pathlib.Path(path).stem
def to_json(self) -> Any:
return json.dumps(self.__dict__, default=str, indent=4)
def to_dict(self) -> dict[str,Any]:
return self.__dict__
@staticmethod
def calc_size_unit(size) -> str:
byte_units = ["Bytes", "KB", "MB", "GB", "TB"]
size_unit = ""
for unit in byte_units:
if size < 1024.0:
size_unit = f"{round(size, 2)} {unit}"
break
else:
size /= 1024.0
return size_unit
if __name__ == '__main__':
fileUtil = FileUtils()
fileUtil.read_file('F:\\[01]project\\commonStandardTerm\\upload\\(붙임)공공데이터 공통표준용어(2022.7월).xlsx')
print(fileUtil.to_dict())
3. ExcelUtils class 만들기
이제 위에서 만든 FileUtils를 상속 받는 ExcelUtils class를 생성해 줍니다.
ExcelUtils는 파일 정보와 함께 엑셀 파일에 대한 간단한 정보를 멤버 변수에 저장합니다.
1. 엑셀파일 객체
2. 엑셀 시트 리스트 객체
3. 엑셀 시트 이름 리스트
4. DataFrame dictionary
"""
ExcelUtils.py
@제목: 엑셀 정보 파일
@설명: 엑셀 정보 Class 파일
작성일자 작성자
-----------------------
2024.03.15 hiio420
"""
from typing import List
import pandas as pd
from openpyxl.worksheet._read_only import ReadOnlyWorksheet
from pandas import ExcelFile, DataFrame
from libs.utils.FileUtils import FileUtils
class ExcelUtils(FileUtils):
def __init__(self):
super().__init__()
self.xlsx_file: ExcelFile | None = None
self.sheets: List[ReadOnlyWorksheet] | None = None
self.sheet_names: List[str] | None = None
self.df_dict: dict[int, DataFrame] | None = None
def read_excel(self, file_path) -> dict[int, DataFrame]:
self.read_file(file_path)
self.xlsx_file = ExcelFile(file_path)
self.sheets = self.xlsx_file.book.worksheets
self.sheet_names = [sheet.title for sheet in self.sheets if sheet.sheet_state == "visible"]
self.df_dict = {i: pd.read_excel(self.path, sheet_name=self.sheet_names[i]).fillna("") for i, n in
enumerate(self.sheet_names)}
return self.df_dict
if __name__ == '__main__':
excelUtils = ExcelUtils()
excelUtils.read_excel('F:\\[01]project\\commonStandardTerm\\upload\\(붙임)공공데이터 공통표준용어(2022.7월).xlsx')
print(excelUtils.to_dict())
이전과 달라진 점이 있다면 숨긴 처리된 시트이름도 모두 가져왔으나, sheet_state를 통해서 보이도록 설정된 시트들 이름만 가져와 dictionay를 만든다는 점입니다.
4. main.py 적용
이제 기본에 있던 코드를 삭제하고 새로 만든 ExcelUtils class로 대체합니다.
"""
main.py
@제목: 메인 실행 파일
@설명: 메인 실행 파일
작성일자 작성자
-----------------------
2024.03.14 hiio420
"""
import math
from fastapi import FastAPI
from libs import ExcelUtils
# 엑셀 파일 불러오기
file_path = "F:\\[01]project\\commonStandardTerm\\upload\\(붙임)공공데이터 공통표준용어(2022.7월).xlsx"
excel_utils = ExcelUtils()
df_dict = excel_utils.read_excel(file_path)
app = FastAPI()
@app.get("/")
def read_root(data_id: int = 0, page: int = 1, limit: int = 10):
if page < 1:
page = 0
if limit < 10:
limit = 10
df = df_dict[data_id]
first_idx = (page - 1) * limit
last_idx = first_idx + limit - 1
total_size = df.shape[0]
first_page = 1
last_page = math.ceil(total_size / limit)
df_sliced = df[first_idx:last_idx + 1].copy()
df_sliced["번호"] = [i for i in range(total_size - (page - 1) * limit, total_size - (page - 1) * limit - df_sliced.shape[0], -1)]
data = []
if df_sliced.shape[0] != 0:
data = df_sliced.to_dict("records")
return {"sheetName": excel_utils.sheet_names[data_id], "data_id": data_id, "page": page, "limit": limit,
"first_page": first_page, "last_page": last_page, "firstIdx": first_idx, "lastIdx": last_idx,
"totalSize": total_size,
"data": data}