[기타] 엑셀 다운로드

2025. 8. 8. 10:02·기능/기타
반응형
  const handleExcelDownLoad = async () => {
    try {
      const response = await fetch(
        `${baseURL}/api/admin/work/export_excel?company_id=${companyId}`,
        {
          method: "GET",
          credentials: "include",
        },
      );

      const blob = await response.blob();
      const url = window.URL.createObjectURL(blob);

      const a = document.createElement("a");
      a.href = url;
      const now = new Date();

      const pad = (n: number) => String(n).padStart(2, "0");

      // 날짜 + 시간 → "20250808_153012" 형태로 포맷
      const timestamp = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(
        now.getDate(),
      )}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(
        now.getSeconds(),
      )}`;

      // 파일명 구성
      const fileName = `${companyName}_${timestamp}.xlsx`;

      // 적용
      a.download = fileName;
      document.body.appendChild(a);
      a.click();
      a.remove();

      window.URL.revokeObjectURL(url);
    } catch (error) {
      console.error("Excel 다운로드 실패:", error);
    }
  };
async def export_excel(company_id: int, db):
    try:
        original_version = aliased(data.content_version)
        latest_version = aliased(data.content_version)
		
        # 글 생성 초안 가져오기
        version1_rows = (
            db.query(
                data.content_version.content_id,
                data.content_version.field_name,
                data.content_version.content_text,
            )
            .filter(data.content_version.version == 1)
            .all()
        )

		original_text_map = defaultdict(list)
        for row in version1_rows:
            field = f"[{row.field_name}]" if row.field_name else ""
            text = f"{field}\n{row.content_text}"
            original_text_map[row.content_id].append(text)

        # 문자열 결합
        for content_id in original_text_map:
            original_text_map[content_id] = "\n\n".join(original_text_map[content_id])

		# 최종본 가져오기
        latest_subq = (
            db.query(
                latest_version.content_id.label("content_id"),
                latest_version.field_name,
                latest_version.content_text.label("final_text"),
            )
            .filter(latest_version.version == 7)
            .subquery()
        )

        results = (
            db.query(
                data.content.id,
                data.content.main_content,
                data.content.sub_content,
                data.content.title,
                data.content.status,
                data.user.user_name,
                data.user.user_email,
                data.department.department_name,
                data.content.last_modified_at,
                data.content.created_at,
                latest_subq.c.final_text,
            )
            .join(data.content_type, data.content.content_type_id == data.content_type.id)
            .join(data.company, data.content_type.company_id == data.company.id)
            .join(data.user, data.content.user_id == data.user.id)
            .join(data.department, data.user.department_id == data.department.id)
            .outerjoin(latest_subq, latest_subq.c.content_id == data.content.id)
            .filter(data.company.id == company_id)
            .all()
        )
        
        status_map = {"temp": "임시저장", "progress": "작업중", "done":"작업완료"}
        
        export_rows = []
        for row in results:
            export_rows.append({
                "대분류": row.main_content or "",
                "소분류": row.sub_content or "",
                "제목": row.title or "제목없음",
                "생산자": row.user_name,
                "소속부서": row.department_name,
                "마지막 수정일": row.last_modified_at.strftime("%Y-%m-%d %H:%M") if row.last_modified_at else "",
                "상태": status_map[row.status.value],
                "초안": original_text_map.get(row.id, ""),
                "생성된 글": row.final_text or "생성된 글이 존재하지 않습니다.",
            })

        if not export_rows:
            # 데이터가 없는 경우 빈 엑셀 파일 생성
            df = pd.DataFrame(columns=[
                "대분류", "소분류", "제목", "생산자",
                "소속부서", "마지막 수정일", "상태",
                "초안", "생성된 글",
            ])
        else:
            df = pd.DataFrame(export_rows)

        # BytesIO 객체 생성
        output = io.BytesIO()
        
        # Excel 파일 생성
        with pd.ExcelWriter(output, engine='openpyxl') as writer:
            df.to_excel(writer, index=False, sheet_name="Exported Contents")
            
            # 워크시트 가져오기
            worksheet = writer.sheets["Exported Contents"]
            from openpyxl.styles import Alignment
            from openpyxl.utils import get_column_letter
            center_columns = ["대분류", "소분류", "제목", "생산자",
                "소속부서", "마지막 수정일", "상태"]
            
            column_name_to_index = {name: idx for idx, name in enumerate(df.columns, start=1)}

            for col_name in center_columns:
                col_idx = column_name_to_index.get(col_name)
                if not col_idx:
                    continue
                col_letter = get_column_letter(col_idx)
                for row in range(2, worksheet.max_row + 1):
                    worksheet[f"{col_letter}{row}"].alignment = Alignment(horizontal="center")
            
            # 열 너비 자동 조정
            for column in worksheet.columns:
                max_length = 0
                column_letter = column[0].column_letter
                for cell in column:
                    try:
                        if len(str(cell.value)) > max_length:
                            max_length = len(str(cell.value))
                    except:
                        pass
                adjusted_width = min(max_length + 10, 50)  # 최대 50으로 제한
                worksheet.column_dimensions[column_letter].width = adjusted_width

        # 포인터를 처음으로 이동
        output.seek(0)
        
        # 파일명에 타임스탬프 추가
        from datetime import datetime
        timestamp = datetime.now().strftime("%Y%m%d")
        filename = f"contents_{timestamp}.xlsx"

        return StreamingResponse(
            output,
            media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            headers={
                "Content-Disposition": f"attachment; filename={filename}",
                "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
            },
        )
        
    except Exception as e:
        import traceback
        traceback.print_exc()
        return JSONResponse(
            status_code=500,
            content={"error": f"Excel export failed: {str(e)}"}
        )
반응형

'기능 > 기타' 카테고리의 다른 글

[RTE] React Tiptap 에디터 적용하기 2  (7) 2026.01.18
[RTE] React Tiptap 에디터 적용하기  (0) 2026.01.07
[SMTP] 구글 이메일 발송  (3) 2025.08.08
'기능/기타' 카테고리의 다른 글
  • [RTE] React Tiptap 에디터 적용하기 2
  • [RTE] React Tiptap 에디터 적용하기
  • [SMTP] 구글 이메일 발송
그낙이
그낙이
시작은 삽질이지만, 끝은 지식입니다.
  • 그낙이
    개발 삽질 일지
    그낙이
  • 전체
    오늘
    어제
    • 분류 전체보기 (71)
      • 서버 (12)
        • 터미널 기본기 (4)
        • AWS (3)
        • Linux (5)
      • 아키텍처 (3)
      • 기능 (19)
        • 로그인 (4)
        • API (5)
        • 앱 (5)
        • 기타 (4)
      • 자유로운 개발일지 (37)
        • APP (4)
        • AI (7)
        • 직링 (19)
        • 자동매매 (6)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    apple developer
    자동매매
    자동화 도구
    개발자 도구 우회
    kotlin
    FastAPI
    Capacitor
    nginx
    챗봇 만들기
    직링
    앱
    챗봇
    puppeteer
    비트코인
    소셜 로그인
    콘서트
    fiddler
    웹소켓
    티켓
    코인
    IAP
    apple connect store
    EC2
    인앱 결제
    퍼피티어
    linux
    GPT
    업비트
    개발자 도구
    예매
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
그낙이
[기타] 엑셀 다운로드
상단으로

티스토리툴바