From f6ba39d4b37a30ddd1b15b138725328e383ef8e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EA=B2=BD=EB=AF=BC?= Date: Thu, 19 Mar 2026 13:54:28 +0900 Subject: [PATCH] Upload report/processor.py with corrected encoding/content --- .../handlers/report/processor.py | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 03.Code/geulbeot_업로드용/handlers/report/processor.py diff --git a/03.Code/geulbeot_업로드용/handlers/report/processor.py b/03.Code/geulbeot_업로드용/handlers/report/processor.py new file mode 100644 index 0000000..ed2085d --- /dev/null +++ b/03.Code/geulbeot_업로드용/handlers/report/processor.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +""" +리포트(report) 처리기 +- 보고서 생성 +- 이미지 경로 변환 +""" +import os +import re +from pathlib import Path +from flask import session + +from handlers.common import call_claude, extract_html, load_prompt, client +from converters.pipeline.router import process_document, convert_image_paths + + +class ReportProcessor: + """리포트 처리기 클래스""" + + def __init__(self): + self.prompts_dir = Path(__file__).parent / 'prompts' + + def _load_prompt(self, filename: str) -> str: + """프롬프트 파일 로드""" + return load_prompt(str(self.prompts_dir), filename) + + def generate(self, content: str, options: dict) -> dict: + """리포트 생성""" + try: + if not content.strip(): + return {'error': '콘텐츠가 없습니다.'} + + # 템플릿 스타일 로드 + template_id = options.get('template_id') + if template_id: + from handlers.template import TemplateProcessor + template_processor = TemplateProcessor() + style = template_processor.get_style(template_id) + if style and style.get('css'): + options['template_css'] = style['css'] + + # 이미지 경로 변환 + processed_html = convert_image_paths(content) + + # router를 통한 문서 처리 및 스타일 적용 + result = process_document(processed_html, options) + + if result.get('success'): + session['original_html'] = content + session['current_html'] = result.get('html', '') + + return result + + except Exception as e: + import traceback + return {'error': str(e), 'trace': traceback.format_exc()} + + def refine(self, feedback: str, current_html: str, original_html: str = '') -> dict: + """피드백 반영""" + try: + if not feedback.strip(): + return {'error': '피드백 내용이 없습니다.'} + + if not current_html: + return {'error': '현재 HTML이 없습니다.'} + + refine_prompt = f"""현재 HTML 리포트를 수정해줘. + +사용자의 피드백 내용을 반영하여 수정된 HTML 리포트를 작성해줘. + +## 규칙 +1. 피드백 내용에 명시된 부분만 수정 +2. **레이아웃 구조(sheet, body-content, page-header 등)는 절대 변경하지 말 것** +3. 전체 HTML 코드로 응답 ( ~ ) +4. 코드 블록(```) 없이 순수 HTML만 응답 + +## 현재 HTML +{current_html} + +## 사용자 피드백 +{feedback} + +--- +사용자의 피드백 내용을 반영하여 수정된 HTML을 작성해줘.""" + + response = call_claude("", refine_prompt, max_tokens=8000) + new_html = extract_html(response) + + session['current_html'] = new_html + + return { + 'success': True, + 'html': new_html + } + + except Exception as e: + return {'error': str(e)} + + def refine_selection(self, current_html: str, selected_text: str, user_request: str) -> dict: + """선택 영역 부분 수정 (텍스트 수정 또는 레이아웃 변경)""" + try: + if not current_html or not selected_text or not user_request: + return {'error': '필수 정보가 누락되었습니다.'} + + message = client.messages.create( + model="claude-3-5-sonnet-20240620", + max_tokens=8000, + messages=[{ + "role": "user", + "content": f"""HTML 리포트의 특정 부분을 수정해줘. + +## 현재 전체 코드 (참고용): +{current_html[:5000]} + +## 선택한 텍스트 영역: +"{selected_text}" + +## 수정 요청 사항: +{user_request} + +## 규칙 +1. **외부 레이아웃 구조(sheet, body-content, page-header, page-footer)는 변경하지 말 것** +2. 선택 영역의 내용만 수정하되, 주변 HTML 태그와 조화롭게 수정 +3. 수정 사항의 특성에 따라 다음 중 하나로 응답: + - TEXT: 텍스트 내용만 수정 (태그 내부 텍스트 변경, 문구 추가 또는 삭제, 오타 수정 등) + - STRUCTURE: HTML 구조 변경 포함 (표 생성, 리스트 추가 등) + +4. 결과물은 다음과 같은 형식으로만 응답: + +TYPE: (TEXT 또는 STRUCTURE) +CONTENT: +(수정된 내용 - 선택 영역의 태그를 포함한 수정 결과) + +5. TEXT 타입의 경우: 문구만 수정 (HTML 태그 포함, 선택 영역의 태그 포함) +6. STRUCTURE 타입의 경우: 새로운 구조의 코드 (레이아웃 구조 X) +7. 결과 이외의 설명 금지 (~입니다, ~함 등 금지) +""" + }] + ) + + result = message.content[0].text + result = result.replace('```html', '').replace('```', '').strip() + + edit_type = 'TEXT' + content = result + + if 'TYPE:' in result and 'CONTENT:' in result: + type_line = result.split('CONTENT:')[0] + if 'STRUCTURE' in type_line: + edit_type = 'STRUCTURE' + content = result.split('CONTENT:')[1].strip() + + return { + 'success': True, + 'type': edit_type, + 'html': content + } + + except Exception as e: + return {'error': str(e)}