""" 결과 출력 관련 유틸리티 함수 모음 - 카테고리 항목 제거 - HTML 테이블 생성 - 엑셀 파일 생성 """ import pandas as pd import tempfile import os import threading import time import logging # 로깅 설정 logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') handler = logging.StreamHandler() handler.setFormatter(formatter) logger.addHandler(handler) # 임시 파일 추적 리스트 _temp_files = [] def create_table_without_checkboxes(df): """DataFrame을 HTML 테이블로 변환 - 키워드 클릭 시 네이버 쇼핑 이동 기능 추가""" if df.empty: return "

검색 결과가 없습니다.

" # === 수정된 부분: 카테고리 관련 열 제거 === df_display = df.copy() # "상품 등록 카테고리(상위100위)" 또는 "관련 카테고리", "카테고리 항목" 열이 있으면 제거 columns_to_remove = ["상품 등록 카테고리(상위100위)", "관련 카테고리", "카테고리 항목"] for col in columns_to_remove: if col in df_display.columns: df_display = df_display.drop(columns=[col]) logger.info(f"테이블에서 '{col}' 열 제거됨") # HTML 테이블 스타일 정의 - Z-INDEX 수정 html = ''' ''' # === 수정된 부분: 열 이름과 클래스 매핑 - 카테고리 관련 제거 === col_mapping = { "순번": "col-seq", "조합 키워드": "col-keyword", "PC검색량": "col-pc", "모바일검색량": "col-mobile", "총검색량": "col-total", "검색량구간": "col-range", "키워드 사용자순위": "col-rank", "키워드 사용횟수": "col-count" # 카테고리 관련 매핑 제거됨 } # 테이블 컨테이너 시작 html += '
' # 단일 테이블 구조로 변경 (헤더는 position: sticky로 고정) html += '
' html += '' # colgroup으로 열 너비 정의 html += '' html += f'' for col in df_display.columns: col_class = col_mapping.get(col, "") html += f'' html += '' # 테이블 헤더 html += '' html += '' html += f'' for col in df_display.columns: col_class = col_mapping.get(col, "") html += f'' html += '' html += '' # 테이블 본문 html += '' for idx, row in df_display.iterrows(): html += '' # 순번 표시 - 1부터 시작하는 순차적 번호 html += f'' # 데이터 셀 추가 for col in df_display.columns: col_class = col_mapping.get(col, "") value = str(row[col]) if col == "키워드 사용자순위": # 긴 텍스트의 셀은 그대로 표시 (줄바꿈 허용) html += f'' elif len(value) > 30: # 다른 긴 텍스트는 hover로 전체 표시 html += f'' else: # 일반 텍스트 html += f'' html += '' html += '' html += '
순번{col}
{idx + 1}{value}
{value[:30]}...
{value}
' html += '
' # data-container 닫기 html += '
' # table-container 닫기 return html def cleanup_temp_files(delay=300): """임시 파일 정리 함수""" global _temp_files def cleanup(): time.sleep(delay) # 지정된 시간 대기 temp_files_to_remove = _temp_files.copy() _temp_files = [] for file_path in temp_files_to_remove: try: if os.path.exists(file_path): os.remove(file_path) logger.info(f"임시 파일 삭제: {file_path}") except Exception as e: logger.error(f"파일 삭제 오류: {e}") # 새 스레드 시작 threading.Thread(target=cleanup, daemon=True).start() def download_keywords(df, auto_cleanup=True, cleanup_delay=300): """키워드 데이터를 엑셀 파일로 다운로드 - 카테고리 항목 제거""" global _temp_files if df is None or df.empty: return None # 임시 파일로 저장 temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.xlsx') temp_file.close() filename = temp_file.name # 임시 파일 추적 목록에 추가 _temp_files.append(filename) # === 수정된 부분: 카테고리 관련 열 제거 === df_export = df.copy() # 카테고리 관련 열들 제거 columns_to_remove = ["상품 등록 카테고리(상위100위)", "관련 카테고리", "카테고리 항목"] for col in columns_to_remove: if col in df_export.columns: df_export = df_export.drop(columns=[col]) logger.info(f"엑셀 내보내기에서 '{col}' 열 제거됨") # 키워드 데이터를 엑셀 파일로 저장 with pd.ExcelWriter(filename, engine='xlsxwriter') as writer: # 키워드 목록 시트 df_export.to_excel(writer, sheet_name='키워드 목록', index=False) # 열 너비 조정 - 카테고리 열 제거 후 조정 worksheet = writer.sheets['키워드 목록'] worksheet.set_column('A:A', 20) # 조합 키워드 열 worksheet.set_column('B:B', 12) # PC검색량 열 worksheet.set_column('C:C', 12) # 모바일검색량 열 worksheet.set_column('D:D', 12) # 총검색량 열 worksheet.set_column('E:E', 12) # 검색량구간 열 worksheet.set_column('F:F', 20) # 키워드 사용자순위 열 worksheet.set_column('G:G', 12) # 키워드 사용횟수 열 # 카테고리 열들 제거로 H, I 열 설정 제거됨 # 헤더 형식 설정 header_format = writer.book.add_format({ 'bold': True, 'bg_color': '#009879', 'color': 'white', 'border': 1 }) # 헤더에 형식 적용 for col_num, value in enumerate(df_export.columns.values): worksheet.write(0, col_num, value, header_format) logger.info(f"엑셀 파일 생성: {filename}") # 파일 자동 정리 옵션 if auto_cleanup: # 별도 정리 작업 요청 없이 추적 목록에 추가만 하여 일괄 처리 pass return filename def register_cleanup_handlers(): """앱 종료 시 정리를 위한 핸들러 등록""" import atexit def cleanup_all_temp_files(): global _temp_files for file_path in _temp_files: try: if os.path.exists(file_path): os.remove(file_path) logger.info(f"종료 시 임시 파일 삭제: {file_path}") except Exception as e: logger.error(f"파일 삭제 오류: {e}") _temp_files = [] # 앱 종료 시 실행될 함수 등록 atexit.register(cleanup_all_temp_files)