728x90

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}


728x90

+ Recent posts