From 47b7ecf34e5392c17a488bb225444014bfcc4467 Mon Sep 17 00:00:00 2001 From: chan Date: Fri, 1 Aug 2025 11:23:40 +0900 Subject: [PATCH] =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=84=98?= =?UTF-8?q?=EB=B2=84=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 2 + workspace/app.py | 127 +++++++++++++++++++++++++++++---------------- 2 files changed, 85 insertions(+), 44 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 468efea..44d41e1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,6 +4,8 @@ version: '3.8' services: ui: image: ocr-comparison-ui + volumes: + - ./workspace:/workspace build: context: . dockerfile: dockerfile # Dockerfile의 상대 경로를 직접 지정 diff --git a/workspace/app.py b/workspace/app.py index 90f625f..ae3e10d 100644 --- a/workspace/app.py +++ b/workspace/app.py @@ -1,4 +1,4 @@ -# app.py (파일 업로드 지원 버전) +# app.py (개별 네비게이션 및 선택 드롭다운 동시 지원) import streamlit as st import json from pathlib import Path @@ -12,19 +12,15 @@ def match_uploaded_files(doc_files, json_files): 결과를 딕셔너리로 반환합니다. """ matched_pairs = {} - - # 각 파일 목록을 이름(stem)을 키로 하는 딕셔너리로 변환 docs_map = {Path(f.name).stem: f for f in doc_files} jsons_map = {Path(f.name).stem: f for f in json_files} - # 문서 파일 기준으로 JSON 파일 찾기 for stem, doc_file in docs_map.items(): if stem in jsons_map: matched_pairs[stem] = { "doc_file": doc_file, "json_file": jsons_map[stem] } - return matched_pairs def display_pdf(file_object): @@ -32,7 +28,6 @@ def display_pdf(file_object): 업로드된 파일 객체(UploadedFile)를 읽어 PDF를 표시합니다. """ try: - # 파일 포인터를 처음으로 되돌림 (중요) file_object.seek(0) base64_pdf = base64.b64encode(file_object.read()).decode('utf-8') pdf_display = f'' @@ -40,6 +35,22 @@ def display_pdf(file_object): except Exception as e: st.error(f"PDF 파일을 표시하는 중 오류가 발생했습니다: {e}") +# --- 콜백 함수 --- + +def handle_nav_button(direction, total_files): + """이전/다음 버튼 클릭을 처리하는 콜백""" + if direction == "prev" and st.session_state.current_index > 0: + st.session_state.current_index -= 1 + elif direction == "next" and st.session_state.current_index < total_files - 1: + st.session_state.current_index += 1 + +def handle_selectbox_change(): + """selectbox 변경을 처리하는 콜백""" + selected_basename_with_index = st.session_state.selectbox_key + # "1. filename" 형식에서 인덱스만 추출 + new_index = int(selected_basename_with_index.split('. ', 1)[0]) - 1 + st.session_state.current_index = new_index + # --- 메인 UI 로직 --- def main(): @@ -76,50 +87,78 @@ def main(): st.warning("업로드된 파일 중 일치하는 문서-JSON 쌍을 찾을 수 없습니다. 파일 이름(확장자 제외)이 동일한지 확인하세요.") return - # --- 2. 파일 선택 --- - st.sidebar.header("파일 선택") - sorted_basenames = sorted(list(matched_files.keys())) + # --- 2. 파일 네비게이션 및 선택 --- + st.sidebar.header("파일 탐색") - selected_basename = st.sidebar.selectbox( - "비교할 파일을 선택하세요.", - sorted_basenames + sorted_basenames = sorted(list(matched_files.keys())) + total_files = len(sorted_basenames) + + # 세션 상태 초기화 + if 'current_index' not in st.session_state: + st.session_state.current_index = 0 + + # 인덱스가 유효한 범위를 벗어나지 않도록 조정 + st.session_state.current_index = max(0, min(st.session_state.current_index, total_files - 1)) + + # Selectbox + display_options = [f"{i + 1}. {name}" for i, name in enumerate(sorted_basenames)] + st.selectbox( + "파일을 직접 선택하세요:", + options=display_options, + index=st.session_state.current_index, + key='selectbox_key', # 콜백에서 값을 참조하기 위한 키 + on_change=handle_selectbox_change ) - if selected_basename: - st.header(f"🔎 비교 결과: `{selected_basename}`") + # 네비게이션 컨트롤 + col1, col2, col3 = st.sidebar.columns([1, 2, 1]) + + col1.button( + "◀ 이전", + on_click=handle_nav_button, + args=("prev", total_files), + use_container_width=True + ) + col2.markdown(f"

{st.session_state.current_index + 1} / {total_files}

", unsafe_allow_html=True) + col3.button( + "다음 ▶", + on_click=handle_nav_button, + args=("next", total_files), + use_container_width=True + ) + + # --- 3. 결과 표시 --- + current_basename = sorted_basenames[st.session_state.current_index] + st.header(f"🔎 비교 결과: `{current_basename}`") + + selected_pair = matched_files[current_basename] + doc_file = selected_pair["doc_file"] + json_file = selected_pair["json_file"] + + res_col1, res_col2 = st.columns(2) + with res_col1: + st.subheader(f"원본 문서: `{doc_file.name}`") + doc_suffix = Path(doc_file.name).suffix.lower() - selected_pair = matched_files[selected_basename] - doc_file = selected_pair["doc_file"] - json_file = selected_pair["json_file"] + if doc_suffix == ".pdf": + display_pdf(doc_file) + else: + st.image(doc_file, caption=f"원본 이미지: {doc_file.name}", use_container_width=True) - # --- 결과 표시 --- - col1, col2 = st.columns(2) - with col1: - st.subheader(f"원본 문서: `{doc_file.name}`") - doc_suffix = Path(doc_file.name).suffix.lower() + with res_col2: + st.subheader(f"추출된 데이터: `{json_file.name}`") + try: + json_file.seek(0) + data = json.load(json_file) - if doc_suffix == ".pdf": - display_pdf(doc_file) - elif doc_suffix in ['.png', '.jpg', '.jpeg']: - st.image(doc_file, caption=f"원본 이미지: {doc_file.name}", use_container_width=True) - else: - st.warning("지원하지 않는 문서 형식입니다.") - - with col2: - st.subheader(f"추출된 데이터: `{json_file.name}`") - try: - # 파일 포인터를 처음으로 되돌림 - json_file.seek(0) - data = json.load(json_file) + result_to_display = data[0] if isinstance(data, list) and data else data + + if isinstance(result_to_display, dict) and 'fields' in result_to_display: + del result_to_display['fields'] - result_to_display = data[0] if isinstance(data, list) and data else data - - if isinstance(result_to_display, dict) and 'fields' in result_to_display: - del result_to_display['fields'] - - st.json(result_to_display) - except Exception as e: - st.error(f"JSON 파일을 읽거나 처리하는 중 오류가 발생했습니다: {e}") + st.json(result_to_display) + except Exception as e: + st.error(f"JSON 파일을 읽거나 처리하는 중 오류가 발생했습니다: {e}") if __name__ == "__main__": - main() \ No newline at end of file + main()