VOC_Monitor/app/controllers/report_manager.py

284 lines
11 KiB
Python

"""
보고서 처리 관리자 (Report Manager)
AppController의 보고서 생성, 인쇄, PDF 변환 로직을 전담합니다.
사용자의 보고서 생성 요청을 받아 ReportService를 호출하고,
결과를 UI에 표시하는 중재자 역할을 수행합니다.
주요 기능:
- 보고서 생성 요청 처리
- 기존 파일 존재 시 사용자 선택 다이얼로그
- 부분 실패 에러 처리 (열차번호 조회 실패 등)
- HWP 인쇄 기능
- PDF 변환 기능
아키텍처:
AppController
├── ReportManager (보고서 처리)
│ └── ReportService (실제 생성)
└── ...
작성자: KH.Choi
최종 수정: 2026-02-17
버전: 1.0 (Controller에서 분리)
"""
import os
from tkinter import messagebox
from core.exceptions import ReportError, is_partial_failure
class ReportManager:
"""
보고서 생성 및 처리 관리자
보고서 생성, 인쇄, PDF 변환 등의 모든 보고서 관련 작업을 처리합니다.
에러 처리와 사용자 피드백을 포함하여 안정적인 보고서 생성을 보장합니다.
Attributes:
controller: AppController 인스턴스 (의존성 주입)
report_service: ReportService 인스턴스
logger: 로거 인스턴스
root: Tkinter 루트 윈도우
settings: 설정 딕셔너리
주요 메서드:
request_create_report: 보고서 생성 요청 진입점
request_print_voc: 인쇄 요청
request_create_pdf: PDF 생성 요청
사용 예시:
>>> report_mgr = ReportManager(controller)
>>> report_mgr.request_create_report(voc_data, parent_window)
"""
def __init__(self, controller):
"""
ReportManager 초기화
Args:
controller (AppController): 메인 컨트롤러 인스턴스
"""
self.controller = controller
self.report_service = controller.report_service
self.logger = controller.logger
self.root = controller.root
self.settings = controller.settings
# ========================================================================
# 보고서 생성 (Report Generation)
# ========================================================================
def request_create_report(self, data: dict, parent=None):
"""
보고서 생성 요청을 받아 옵션 다이얼로그를 엽니다.
역명 정보를 기반으로 기본 사업소를 유추하고,
사용자가 옵션을 선택하면 실제 생성 로직을 실행합니다.
Args:
data (dict): VOC 데이터
- id: VOC ID
- title: 제목
- content: 내용
- date: 날짜
- department: 부서
- station (optional): 역명
parent (Widget, optional): 부모 윈도우
Raises:
Exception: 다이얼로그 생성 실패 시
"""
try:
# 역명에 따른 기본 사업소 유추
station = data.get('station', '')
if station:
if any(x in station for x in ["신평", "다대포", "중앙", "부산역"]):
data['office'] = "신평차량사업소"
elif any(x in station for x in ["노포", "범어사", "동래", "연산"]):
data['office'] = "노포차량사업소"
# 콜백 함수: 다이얼로그에서 '생성'을 누르면 실행
def actual_generate(data, options):
self._generate_report_with_options(data, options, parent)
from view.dialogs.report_option_dialog import ReportOptionDialog
target_parent = parent if parent else self.root
ReportOptionDialog(target_parent, self.controller, data, callback=actual_generate)
except Exception as e:
self.logger.error(f"보고서 생성 요청 실패: {e}", exc_info=True)
messagebox.showerror("오류", f"보고서 생성을 시작할 수 없습니다.\n{e}")
def _generate_report_with_options(self, data: dict, options: dict, parent):
"""
보고서 생성 실행 (내부 메서드)
사용자가 선택한 옵션을 데이터에 병합하고,
ReportService를 호출하여 HWP 보고서를 생성합니다.
에러 처리:
- 부분 실패 (열차번호 조회 실패): 경고만 표시하고 계속
- 전체 실패 (템플릿 없음 등): 에러 표시 및 중단
- 기존 파일 존재: 사용자 선택 다이얼로그 표시
Args:
data (dict): VOC 데이터
options (dict): 보고서 옵션
- office: 사업소
- team: 팀
- reporter_name: 보고자
- position: 직위
- action_taken: 조치사항
parent (Widget): 부모 윈도우
"""
try:
# 사업소별 전화번호 매핑
tel_map = self.settings.get('master_data', {}).get('office_tel_map', {})
office = options.get('office', '')
options['reporter_tel'] = tel_map.get(office, "000-0000")
# 옵션을 데이터에 병합
data.update(options)
# ReportService 호출
result = self.report_service.create_hwp_report(data)
# 결과 처리
if isinstance(result, tuple) and len(result) == 2:
status, msg_or_path = result
if status == "FILE_EXISTS":
# 기존 파일 존재
self._handle_existing_report(msg_or_path, data, parent)
elif status:
# 정상 생성 완료
file_path = msg_or_path.split('\n')[1] if '\n' in msg_or_path else msg_or_path
from view.dialogs.report_complete_dialog import ReportCompleteDialog
self.root.after(1000, lambda: ReportCompleteDialog(parent, file_path))
else:
# 생성 실패
messagebox.showerror("보고서 생성 실패", msg_or_path)
else:
messagebox.showerror("오류", "예상치 못한 응답 형식입니다.")
except ReportError as e:
if is_partial_failure(e):
# 부분 실패: 경고만 표시하고 계속
self.logger.warning(f"부분 실패: {e.message}")
messagebox.showwarning("경고", e.message)
else:
# 전체 실패
self.logger.error(f"보고서 생성 실패: {e.message}")
messagebox.showerror("오류", e.message)
except Exception as e:
self.logger.error(f"보고서 생성 중 오류: {e}", exc_info=True)
messagebox.showerror("오류", f"보고서 생성 중 오류가 발생했습니다.\n{e}")
def _handle_existing_report(self, file_path: str, data: dict, parent):
"""
기존 보고서 존재 시 사용자 선택 처리 (내부 메서드)
사용자에게 3가지 선택지를 제공합니다:
- 예: 기존 파일 열기
- 아니오: 새로 생성 (기존 파일 덮어쓰기)
- 취소: 작업 취소
Args:
file_path (str): 기존 파일 경로
data (dict): VOC 데이터
parent (Widget): 부모 윈도우
"""
from tkinter import messagebox as mb
choice = mb.askyesnocancel(
"기존 보고서 발견",
f"동일한 보고서가 이미 존재합니다:\n\n{file_path}\n\n"
"• 예: 기존 파일 열기\n"
"• 아니오: 새로 생성 (기존 파일 덮어쓰기)\n"
"• 취소: 작업 취소"
)
if choice is True:
# 기존 파일 열기
os.startfile(file_path)
from view.dialogs.report_complete_dialog import ReportCompleteDialog
self.root.after(500, lambda: ReportCompleteDialog(parent, file_path))
elif choice is False:
# 새로 생성 (강제 덮어쓰기)
try:
os.remove(file_path)
success2, msg2 = self.report_service.create_hwp_report(data)
if success2:
file_path2 = msg2.split('\n')[1] if '\n' in msg2 else msg2
from view.dialogs.report_complete_dialog import ReportCompleteDialog
self.root.after(1000, lambda: ReportCompleteDialog(parent, file_path2))
else:
messagebox.showerror("보고서 생성 실패", msg2)
except Exception as e:
messagebox.showerror("오류", f"파일 삭제 실패: {e}")
# choice is None (취소): 아무것도 하지 않음
# ========================================================================
# 인쇄 및 PDF (Print & PDF)
# ========================================================================
def request_print_voc(self, data: dict):
"""
인쇄 요청을 받아 ReportService 호출
Windows 기본 프린터로 VOC 상세 내용을 출력합니다.
Args:
data (dict): 인쇄할 VOC 데이터
- id: VOC ID
- title: 제목
- content: 내용
- answer: 답변 (optional)
Raises:
ReportError: 인쇄 실패 시
"""
try:
success, msg = self.report_service.print_voc_detail(data)
if success:
messagebox.showinfo("인쇄 성공", msg)
else:
messagebox.showerror("인쇄 실패", msg)
except ReportError as e:
self.logger.error(f"인쇄 실패: {e.message}")
messagebox.showerror("오류", e.message)
except Exception as e:
self.logger.error(f"인쇄 중 오류: {e}", exc_info=True)
messagebox.showerror("오류", f"인쇄 중 오류가 발생했습니다.\n{e}")
def request_create_pdf(self, data: dict):
"""
PDF 생성 요청을 받아 ReportService 호출
VOC 데이터를 PDF 형식으로 변환하여 저장합니다.
Args:
data (dict): VOC 데이터
- id: VOC ID
- title: 제목
- content: 내용
- date: 날짜
- department: 부서
Raises:
ReportError: PDF 생성 실패 시
"""
try:
success, msg = self.report_service.create_pdf_report(data)
if success:
messagebox.showinfo("PDF 생성 성공", msg)
else:
messagebox.showerror("PDF 생성 실패", msg)
except ReportError as e:
self.logger.error(f"PDF 생성 실패: {e.message}")
messagebox.showerror("오류", e.message)
except Exception as e:
self.logger.error(f"PDF 생성 중 오류: {e}", exc_info=True)
messagebox.showerror("오류", f"PDF 생성 중 오류가 발생했습니다.\n{e}")